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 meansbigint
columns must not exceed 2^53 to avoid loss of the low-order bits. Also, the returned dataset provides just one column, namedGENERATED_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 jythonNone
. -
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.