Skip to content

Utilities

The following functions are useful in any context or scope, and do not fit within another category.

transform()

This function emulates the “expression transform” operations available in Perspective bindings in other binding contexts (expression tags, Vision component bindings, Identity Provider attribute mappings), though can be used in Perspective, too.

transform(valueExpr, transformExpr) returns Object

valueExpr Any expression. It is executed first, and its result is loaded into a thread-local stack, similar to the thread-locals used by the iteration functions.
transformExpr Any expression. This is executed after the thread-local has been set up, enabling this expression to use value() in multiple places. The result of this expression is the result from the function as a whole.

You can treat this function as if the valueExpr was top half of an expression binding that has an expression transform attached, and the transformExpr was the bottom half. See the example below.

value()

This function must be nested in the transformExpr of a tranform() function. It retrieves the saved “top half” value anywhere it is needed within the “bottom half”, without needless re-execution of the “top half” expression.

value([depth]) returns Object

depth Optional number of saved thread locals to skip before retrieving the value, similar to the behavior of it() and idx() when using nested iteration functions. Zero when omitted.

A Perspective expression binding that has an expression transform attached can be reimplemented in a single expression using transform() and value(). Like this Perspective binding expression:

split({path.to.some.string}, '/')

with this transform:

{value}[len({value})-1, 0]

can be combined in a single expressions like so:

transform(
    split({path.to.some.string}, '/'),
    value()[len(value())-1, 0]
)

Note that the expression transform simply has {value} replaced with value()–exactly the same number of characters.

This expression can also be deployed outside of Perspective.

jsonDecode()

This function offers the functionality of Ignition’s native system.util.jsonDecode() scripting function in expressions. (It uses the scripting implemention exposed in the SDK API directly.)

jsonDecode(source) returns Object

source String to be interpreted as JSON to produce and return and object tree.

jsonEncode()

This function offers a robust alternative to Ignition’s native system.util.jsonEncode() scripting function in expressions. Instead of using the buggy scripting implementation exposed in the SDK API, it uses IA’s customized version of GSON that is shipped with the platform. Unlike the scripting interface, this function always delivers a “pretty printed” string.

jsonEncode(object) returns String

object Any object that can be JSONified by GSON, typically including all standard dictionary, list, string, and numeric types, whether from java or jython.

ramp()

Given speed and acceleration limits, compute values on a trajectory from the current value to a target position. Supports separate forward and reverse speeds. Updates on a given poll rate in event driven contexts and scopes, like UI bindings, and at the scan class rate in expression tags.

ramp(pollRate, target, rampRate [, revRampRate [, accel]]) returns Double

pollRate Milliseconds between computations, or zero to short-circuit the target to the output. Polling will cease when the output reaches the target.
target Number that is the final value the expression attempts to reach.
rampRate The positive or forward ramp rate, in units per second. (Not units per poll).
revRampRate Optional negative or reverse ramp rate, in units per second, expressed as a positive number. Same as the positive ramp rate if omitted.
accel Optional maximum change in ramp rate, in units per second squared, or zero to disable acceleration limiting. Disabled if omitted.

Any of these values may be changed on the fly.

tags()

This function provides an alternative to a script transform when values must be retrieved for a variable list of tag paths. When the tag paths are provided in a Dataset, the return value is a Dataset, with two columns: path and value. Otherwise the return value is a list of pairs of path and value. (Where relative paths are allowed, they are expanded for the return value.)

tags(tagPaths [, timing]) returns Dataset or List

tagPaths This argument provides the tagpaths whose values are to be retrieved. If a dataset, its first column supplies the paths. Other columns will be ignored. Otherwise, it must be a sequence or array whose elements are the tag path strings.
timing This argument controls the subscription mode and timing. When zero, no subscription is made and the tags are read with the equivalent of system.tag.readBlocking(), but with a 1000ms timeout (not adjustable). Otherwise, this integer is clipped to the range of 5 to 1000 milliseconds, and each tag is subscribed. When the first subscription value arrives, the function waits this long for more values to arrive before signalling the expression to re-execute. In this mode, the first execution always yields nulls with quality "Good, Uncertain Initial Value". The default is five milliseconds.

toolkitCtx()

There are a number of scope-specific techniques available to retrieve an instance of Ignition’s CommonContext for advanced (and unsupported) scripting tasks. Integration Toolkit now exposes its own scope-specific context to scripts.

system.util.toolkitCtx() returns CommonContext

The return value is a ModuleContext subclass that corresponds to the scope, so it behaves as a GatewayContext in gateway scope, et cetera.

Use with care!

indirectAction()

It is not safe to attach jython listeners or consumer implementations to Ignition platform APIs, as the code in the listener will cause a significant memory leak when the scripting context that created the listener is restarted. This function produces an object that performs call-time lookup of a project library function to execute a listener/consumer action, and that has been coerced into the java type required by the API.

system.util.indirectAction(functionName, proxyFor, projectName={current}) returns Object

functionName A dot-delimited simple name of a function in a project library script. The function must accept the number of arguments expected by the java interface targeted.
proxyFor A platform API interface that defines a single non-default method to be emulated by the specified function. (Not enforced. If an interface has multiple non-default methods, they will all be funneled to the given function for execution.)
projectName The project that contains the desired library script function. When omitted, the current project name is looked up and used. Or the global scripting project is used, if called outside a project in gateway scope.

The object returned by this function proxies the target function without holding any actual jython code. It will always call the latest code version as projects are edited, making it safe for use as a long-lived API listener.

When an object has already been created with precisely the same set of arguments, the returned object is the same as returned by the prior call, managed in a persistent registry. It is a good idea to record in an instance from system.util.globalVarMap that a particular listener has been attached to an API, as many APIs will not collapse identical listeners into a single run-time call. (The .putIfAbsent() method of the BindableVarMap is recommended, where only a non-null return is then attached to the API.)

runPrepInsert()

The Ignition builtin scripting function system.db.runPrepUpdate has the unfortunate limitation of only returning 32-bit integer keys when using its getKey=True option.

This alternative performs the same basic function as runPrepUpdate, with an implicit getKey=True, but returns the JDBC standard’s .getGeneratedKeys() resultset directly, as a PyDataset. This can only be used with databases whose JDBC driver implements this method, and the actual behavior varies by implementation. Some notes:

  • Microsoft SQL Server returns identity columns after coercion to Double. This means bigint columns must not exceed 2^53 to avoid loss of the low-order bits. Also, the returned dataset provides just one column, named GENERATED_KEYS. When a multi-row INSERT is performed, only the last row’s key is returned. When the primary key is a GUID column, the result is a jython None.

  • PostgreSQL returns the entire row produced by the INSERT operation, or multiple entire rows when a multi-row INSERT is performed. The returned rows have the column names and types as defined for the table. This includes UUID columns where the default value is gen_random_uuid().

If your application calls for UUID primary keys, or has a multi-column primary key with automatic defaults, consider using a database brand that has a competent JDBC driver in this regard.

system.db.runPrepInsert(query, args, [database], [tx]) returns PyDataset

query A string containing the SQL to prepare. It must be an INSERT operation. Include ? substitutions as appropriate, as used with any of Ignition's native "Prep" query scripting functions.
args A list of actual value arguments corresponding to the ? substitution placeholders, in order. Supply an empty list if no substitutions are used.
database The name of the database connection to use. In project contexts, this may be omitted, and the project default will be used.
tx An optional string identifying an open transaction. Usage is identical to that for the native system.db.runPrepUpdate.

This function accepts all arguments in keyword form. The skipAudit argument found in native functions is not implemented.

runInGateway()

Numerous operations in Ignition are only possible in Gateway Scope, and will fail if attempted from the designer Script Console or from a Vision Client.

A work-around is to set up a Gateway Message Handler that will perform the desired operation and return the result. Designer and Vision Client scopes can use system.util.sendRequest() to invoke the message handler to perform the desired task. A message handler could even perform function lookups by name to multiplex through a single message handler.

The above doesn’t scale, particularly when multiple requests are needed in parallel.

The subject function is a Jython decorator that eliminates the need for a custom message handler and also handles as many parallel requests as the gateway normally would accept from any other client.

When executed in Gateway Scope, the decorator examines its environment and the decorated function to ensure that it can later be called correctly. (It must be a top level function in a Project Library Script. Not in site packages, nor a method nor any kind of nested function.) But otherwise passes the function through to the module namespace.

When executed in the Designer or a Vision Client scope, the given function is validated as in gateway scope, but the implementation is altered to perform Gateway RPC with the function arguments instead of executing locally. The matching function in gateway scope is invoked and its result (or exception) is returned to the remote caller.

Be sure to save your project before calling any such function.

system.util.runInGateway(f) returns PyFunction

f A Jython Function definition. Typically supplied via decorator syntax. The function must be a top level function in a project library script.

Example:

logger = system.util.getLogger(system.util.getProjectName() + '.' + system.reflect.getModulePath())

@system.util.runInGateway
def gwLogInfof(*args):
    logger.infof(*args)

The above, called as someScript.gwLogInfof(...) within the same project will perform the logging operation in gateway scope, no matter what scope initiates it. Be aware that arguments to RPC functions must be serializable on the network. Objects that are intimately tied to the running JVM in which they live cannot be delivered via RPC.