About Mango Javascript

June 2nd, 2020


Mango JavaScript

Most script editors have an adjacent blue question mark to open the contextual help. In the related items at the bottom of that help is "About Mango JavaScript" which contains the most up to date information for what features exist in that version of Mango.

The Script area is where the script to execute is entered. Scripts can be any valid ECMAScript that would be written within a function.

In addition to the ECMAScript context, globally-accessible functions can be defined, such as max(), min(), avg(), and sum(). (These functions are implemented in an modules such as SST Global Scripts.) To use them, simply call them from your script, for example:

return max(x.value, y.value, z.value);

This returns the maximum of the present values of 'x', 'y', and 'z'. Any number of parameters can be provided to any of these global functions.

Once the script has been entered, click the check mark icon to execute it and attempt to calculate the result.


Point Value Time Objects

Mango stores its data in Point Value Time objects that contain a value and a time. When accessing internal data from Mango it is important to know if you are dealing with a raw value or a Point Value Time.

{
    value: value of object,
    time: timestamp in milliseconds since epoch
}

Time values

The timestamp of value is also available to the script. The following fields can be useful for conversions:

  • p.time - timestamp of the value in milliseconds since the epoch
  • p.millis - 0-999 the millisecond portion of p.time
  • p.second - 0-60
  • p.minute - 0-60
  • p.hour - 0-23
  • p.day - 1-28,31
  • p.dayOfWeek - 1-7 where 1 is Sunday
  • p.dayOfYear - 1-365,366
  • p.month - 1-12
  • p.year - four digits

Context Objects

The script var that represents a point in a script is actually an 'object', in JavaScript terminology. An object is a container of values and functions that can be referenced by their property names. To get a description of the properties available for use in a script var, use the help property, e.g.:

return x.help;

This script works best if the data type is set to alphanumeric, but this is not required. The help property is identical to the toString() function, which is available on all context objects (i.e. not just script vars).

The value property is the present value of the point. The JavaScript type of the value is analogous to its Mango type: Binary become boolean, Numeric becomes float, Multistate becomes integer, and Alphanumeric becomes string.

Each script var also implements additional functions. The objects returned by these functions depend upon the data type of the point the var refers to. Again, the help property can be used to get a description of the returned object's properties. For the "periodType" parameter in all of the functions below, the following pre-defined global variables can be used: SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, and YEAR.

The ago() function returns the value that the point had the given amount of time ago. For example, the call "x.ago(HOUR, 3)" returns the point's value exactly 3 hours ago.

The past() function returns an object containing statistics over the given period ending now. See below for a description of the various statistics objects.

The prev() and previous() functions are identical; the latter is provided for its linguistic completeness. The functions return the same statistically object as past(), but over a different time span. The start and end times are quantized such that they correspond to the period type. For example, if the period type is HOURLY and periods is 1, and the function runs at 18:05, the time span that will be used is from 17:00 (inclusive) to 18:00 (exclusive). If the periods were, say, 3, the time span would be from 15:00 to 18:00. Similarly, MONTH starts the time span at midnight on the first day of the previous month and ends it on the last day of the previous month (when periods is 1). Other period types work the same. A WEEK starts on Monday at midnight in accordance with ISO standards.

The last() function return a list of the latest point value/time objects up to the given limit. The list will never be null, but could have a size from 0 to n depending on how many historical values there actually are. Values are sorted from most newest to oldest so that list.get(0) will return the most recent value. The list is a java.util.List object, and so has all of the methods available in that interface, including get(index) and size(). The get(index) method will throw an ArrayIndexOutOfBoundsException if you ask for an index that is >= the size, so be sure to check the list size before using get. Point value/time objects have "value" and "time" properties.

The lastValue() function returns a single point value/time object, or null if the index is invalid. (Size checking in the list is done automatically, so an ArrayIndexOutOfBoundsException will never be thrown.) lastValue() and lastValue(0) will return the most recent value, lastValue(1) will return the second-most recent value, etc.

The pointValuesBetween(from, to) function return a list of point value/time objects between the given timestamps. The returned values are inclusive of the from time and exclusive of the end time. The list will never be null, but could have a size from 0 to n depending on how many historical values there actually are. Values are sorted in time order so that list.get(0) will return the oldest value. The list is a java.util.List object, and so has all of the methods available in that interface, including get(index) and size(). The get(index) method will throw an ArrayIndexOutOfBoundsException if you ask for an index that is >= the size, so be sure to check the list size before using get. Point value/time objects have "value" and "time" properties.

The pointValuesSince(timestamp) function return a list of point value/time objects since the given timestamp. The returned values are inclusive of the timestamp. The list will never be null, but could have a size from 0 to n depending on how many historical values there actually are. Values are sorted in time order so that list.get(0) will return the oldest value. The list is a java.util.List object, and so has all of the methods available in that interface, including get(index) and size(). The get(index) method will throw an ArrayIndexOutOfBoundsException if you ask for an index that is >= the size, so be sure to check the list size before using get. Point value/time objects have "value" and "time" properties.

The pointValueBefore(timestamp) function returns the nearest point value/time object prior to timestamp, or null if there is not one.

The pointValueAfter(timestamp) function returns the nearest point value/time object after to timestamp, or null if there is not one.

The pointValueAt(timestamp) function returns the a point value/time object that was recorded exactly at the timestamp, or null if there is not one.


Statistical objects

Statistics objects are returned from the past(), prev(), and previous(), getStats(from, to) functions. (See "Context objects" above.) The properties of the object returned depend upon the data type of point upon which they were generated. Time values in objects are stored as integers, but represent the number of milliseconds since midnight Jan 1, 1970.

Note 1: getStats(from, to) operates on timestamps, usage:

  //Get the stats for the past 20 minutes
  var end = new Date();
  var start = new Date(end.getTime() - 1000 * 60 * 20); //20 min before now
  var stats = p1.getStats(start.getTime(), end.getTime());

Note 2: Statistics are returned as Javascript Objects so Mathematical operations must use the .doubleValue() method. For example:

  p1.past(HOUR).minimumValue.doubleValue() + p2.past(HOUR).maximumValue.doubleValue()

The AnalogStatistics object is returned by Numeric points. It contains the following properties:

  • minimumValue: (float) the minimum value the point reached over the period
  • minimumTime: (integer) the time at which the minimum value was reached
  • maximumValue: (float) the maximum value the point reached over the period
  • maximumTime: (integer) the time at which the maximum value was reached
  • average: (float) the average value of the point over the period
  • integral: (float) the integral value of the point over the period
  • sum: (float) the sum of all value updates over the period (appropriate for pulse counting)
  • startValue: the value before or exactly at the period start time
  • firstValue: (float) the first value in the period
  • firstTime: (integer) the time of the first value
  • lastValue: (float) the last value in the period
  • lastTime: (integer) the time of the last value
  • count: (integer) the number of updates over the period
  • delta: (float) the cumulative change in value from firstValue to lastValue
  • periodStartTime (integer) the start time used for the calculation
  • periodEndTime (integer) the end time used for the calculation

For example, the following returns the minimum value of 'n' over the past hour:

n.past(HOUR).minimumValue;

The StartsAndRuntimeList object is returned by Binary and Multistate points. It contains the following properties:

  • periodStartTime (integer) the start time used for the calculation
  • periodEndTime (integer) the end time used for the calculation
  • count (integer) count of the total number of entries
  • startValue: the value before or exactly at the period start time
  • firstValue: the first value in the period
  • firstTime: (integer) the time of the first value
  • lastValue: the last value in the period
  • lastTime: (integer) the time of the last value
  • data: (array) the list of individual StartAndRuntime objects.

Each StartAndRuntime object has the following properties:

  • value: (boolean for Binary, integer for Multistate) the point state to which the remaining properties apply
  • starts: (integer) the number of times the state was entered over the period
  • runtime: (integer) the amount of time in milliseconds the point was in the state over the period
  • proportion: (float) the proportion of the period the point was in the state (runtime / real duration)
  • percentage: (float) proportion * 100

To access a specific StartAndRuntime object in the list, use the get() function. For example, the following returns the proportion of time that 'b' was in state 'false' over the previous 2 months.

return b.past(MONTH, 2).get(false).proportion;

The ValueChangeCounter object is returned by Alphanumeric points. It contains a property changes, which is the number of times the point changed during the period. For example, the following returns the number of times 'a' changed during the previous 45 minutes.

b.previous(MINUTE, 45);

Each ValueChangeCounter has the following properties:

  • periodStartTime (integer) the start time used for the calculation
  • periodEndTime (integer) the end time used for the calculation
  • count (integer) count of the total number of entries
  • startValue: the value before or exactly at the period start time
  • firstValue: the first value in the period
  • firstTime: (integer) the time of the first value
  • lastValue: the last value in the period
  • lastTime: (integer) the time of the last value
  • count: (integer) count of the number of samples
  • changes: (integer) number of unique sample values

Global Utilities

The script context has some utilites that are globally available.


DateTimeUtility

This utility is accessible via DateTimeUtility.{method}.

getOffset(timezoneId, timestamp)

timezoneId - "Europe/Rome" timestamp - ms timestamp of any date return - timezone offset in milliseconds at that point in time

getTimezone()

return - Timezone from where the script is being run.

getUtcOffset(timestamp)

timestamp - ms timestamp of any date return - the UTC offset in milliseconds from where the script is being run

parseDate(format, dateString, timezoneId)

format - See here dateString - String date and is related to above timezoneId - Timezone ID to use return - millisecond date

formatDate(formate, timestamp, timezoneId)

format - See here timestamp - milliseconds since epochtimezoneId - timezone to use return - String formatted date

RuntimeManager

This utility is accessible via RuntimeManager.{method}. The enable/disable methods return a status of:

  • -1 - Does Not Exist
  • 0 - No change in state
  • 1 - Operation successful

Available Methods:

refreshDataPoint(xid)

Refreshing a data point suggests to the Data Source that the point value should be collected ASAP. This will only happen if the underlying data source has implemented this functionality.

xid - Xid for data point

returns:

  • -1 - Point does not exist
  • 0 - Point is not enabled
  • 1 - Suggestion made to data source

isDataSourceEnabled(xid)

xid - Xid for a data source return true if enabled false if not or DNE

enableDataSource(xid)

xid - Xid for a data source, return status

disableDataSource(xid)

xid - Xid for a data source, return status

isDataPointEnabled(xid)

xid - Xid for a data point return true if enabled false if not or DNE or User does not have permissions

Note: a disabled data point is a data point that is not collecting data, which can be due to either the pointbeing disabled or its data source being disabled.

enableDataPoint(xid)

xid - Xid for a data point, return status

disableDataPoint(xid)

xid - Xid for a data point, return status

sleep(milliseconds)

milliseconds- Milliseconds to sleep the script\s thread execution for, nothing returned


Data Point Queries

Data points can be queried by using the DataPointQuery utility. This utility has one method: DataPointQuery.query(stringRql);

DataPointWrapper

The DataPointWrapper returned has some of the basic information on a data point in addition to the Runtime member which is the same as the Object that wraps the context points for a script. The following are members of the DataPointWrapper Object:

  • extendedName - String
  • settable - Boolean
  • deviceName - String
  • enabled - Boolean
  • xid - String
  • name - String
  • unit - String
  • dataSourceName - String
  • dataSourceXid - String
  • runtime - Point Context object

Note: The runtime is the object you would expect to see by adding a point to the script context. This is the object that contains the .ago(), .past() and .prev() methods as described above. Use caution as this member is null if the data point is not enabled.

RQL

RQL is a query language that is highly flexible, more info here. For example to find all points with a name that starts with Volts use this query: like(name, Volts*) Note the use of the asterisk wildcard, this is the default for all Mango RQL. Also to see what is available to use in the query go here.

RQL Examples

This example will query for a set of the first 10 data points who's names contain the word 'boiler' and return an average their values. There is an additional check to ensure that only points that are enabled are used. Note that this could also be done in the RQL statement.

var points = DataPointQuery.query('like(name, *boiler*)&limit(10)');
var average = 0.0;

LOG.info('Found ' + points.size() + ' points to compute average.');

for(var i=0; i<points.size(); i++){
    if(points.get(i).runtime !== null){
        LOG.info('Adding ' + points.get(i).runtime.value + ' to the average.');
        average = average + points.get(i).runtime.value;
    }
}

return average/points.size(); 

Data Source Queries

Data sources can be queried in much the same way. There is one method, DataSourceQuery.query(stringRql); A returned object will contain:

  • name - String
  • xid - String
  • enabled - boolean
  • type - String
  • points - List<DataPointWrapper>

Point Value Queries

While point values and statistics can be gotten through point wrappers, one need use the PointValueQuery utility to easily stream values or collate multiple points' values by time. A second method is provided to optionally roll the data up into rollup periods, and then to pass a part or the whole statistics object to the callback. To see the method signatures, run print(PointValueQuery); from within the scripting environment.


JsonEmport Utility

The JsonEmport utility provides methods to pass JSON or get JSON as one would through an import/export page. A script cannot save itself through the emport utility. If it tried, that item in the JSON will be ignored. This utility is accessible through the context key "JsonEmport" and has the following functions:

  • getFullConfiguration() - String
  • getConfiguration(String configurationKey) - String
  • dataPointQuery(stringRql) - String
  • dataSourceQuery(stringRql) - String
  • doImport(String) - void
  • doImportGetStatus(String) - List<String>

Logging

Logging can be achieved during script testing on the edit page by enabling the logging level desired and then using the appropriate log statement.

LOG.trace('trace');
LOG.debug('debug');
LOG.info('info');
LOG.warn('warn');
LOG.error('error');
LOG.fatal('fatal');

HttpBuilder

HTTP exchanges can be sent and handled from within the script body without using Java directly. The HttpBuilder object provides GET, POST, PUT and DELETE with synchronous callbacks. By default, responses with status 200 will be passed to the resp function if defined, other responses to the err function if defined, and local exceptions to the excp function if defined, or the err function if that is defined instead with status -1. Two forms of usage are supported:

//First format, method chaining
print(HttpBuilder.get("http://localhost:8080/help.htm", {/* user supplied headers */}, {/* user supplied parameters */})
    .err(function(status, headers, content) { //setErrorCallback for linguistic completion
        throw "Request got bad response: " + status;
    }).resp(function(status, headers, content) { //setResponseCallback
        print(content);
        return true;
    }).excp(function(exception) { //setExceptionCallback
        throw exception.getMessage();
    }).execute()); //will print the help page then "true" if the resp function is executed. Otherwise will throw the error.

//Second format, full request
print(HttpBuilder.request({
    path: "http://localhost:8080/help.htm",
    method: "GET",
    headers: {},
    parameters: {},
    //content: "GETs don't have content!",
    err: function(status, headers, content) { //errorCallback for linguistic completion
        throw "Request got bad response: " + status;
    },
    resp: function(status, headers, content) { //responseCallback
        print(content);
        return true; //will print in wrapping print()
    }
    excp: function(exception) { //exceptionCallback
        throw exception.getMessage();
    }
}));

CONTEXT_POINTS

CONTEXT_POINTS is a variable declared in all scripting environments. It is a map of variable names to DataPointRT objects for the points which have been added to the script context.

You also have available:

  • EXTERNAL_POINTS: Map of variable name to point wrapper.
  • EXTERNAL_POINTS_ARRAY: Array of point wrappers.

this is specially helpful when calculating a sum/average/min/max for an arbitrary number of context points.

Check this example to get the sum:

return Java.from(EXTERNAL_POINTS_ARRAY).reduce(function(accum, point) {
    return accum + point.value;
}, 0);

or to get the max value:

return Math.max.apply(null, Java.from(EXTERNAL_POINTS_ARRAY).map(function(point) {
    return point.value;
}));

The difference between these and CONTEXT_POINTS is that they

  1. Do not contain the meta data point itself (for meta data points), only external context points
  2. They contain the wrapper objects, not a DataPointRT

Accessing Mango DAOs and Services

var Common = Java.type("com.serotonin.m2m2.Common"); 
var dataPointService = Common.getBean(Java.type('com.infiniteautomation.mango.spring.service.DataPointService').class);
var dataPoint = dataPointService.get('DP_XID');

More Help

For general discussion on scripting in Mango see here.

Copyright © 2020 Radix IoT, LLC.