Skip to content

Expression-Scripting Connectors

Expression bindings and jython scripting have only one connector in the general case, Ignition’s native runScript() expression function. In Perspective, scripting transforms can also be tied to expressions. Integration Toolkit adds several additional connectors.

The objectScript() and view() expression functions invoke the jython interpreter from an Ignition expression context, with advanced functionality.

The various *VarMap() expression functions pull jython mapping objects into binding scopes, with jython-triggerable binding support.

Scope-Independent Functions

objectScript()

objectScript(expr, [args...]) returns Object

expr A string of code suitable as the right-hand-side of a python assignment statement. If multiple lines, the following lines are expected to be syntactically suitable as a continuation of the right-hand-side expression, and/or more top-level code. Following lines can change the return value by assigning to __retv again.
args Optional arguments to be assembled into a Python tuple and placed in the locals() dictionary of the expression as args. One-line expressions may extract specific values with subscripts or slices, or any part may be passed to library functions as desired. If omitted, a zero-length args tuple will still be present.

In most scopes, expression functions are provided an InteractionListener object which is generally the binding itself. A binding entry will be placed into the expression’s locals() dictionary pointing at the supplied InteractionListener, or None. This can be passed into a library function to permit an advanced function to arbitrarily trigger a binding refresh.

In Vision scope, and certain others, the binding typically has a target attribute pointing at the GUI component, and a targetPropertyName attribute identifying the bound property on that component. Binding objects often have additional useful attributes in the various possible scopes.

In Perspective scope, the binding object typically does not have a target attribute, but self is made available in the locals() dictionary. Unlike runScript() in Perspective scope, session, page, and view are not placed in the locals() dictionary. These are typically attributes of self, though, if needed.

In all scopes, provides a persistent state dictionary in locals(), unique to the expression instance, to carry information from one execution to the next.


view()

view(selectString, fromDataset, [args...]) returns Dataset

selectString A string containing Pseudo-SQL describing the operation(s) to perform on the supplied dataset. See the Pseudo-SQL topic below.
fromDataset The source dataset providing the implied FROM clause.
args Optional arguments to be assembled into a Python tuple and placed in the locals() dictionary of the expression as args. One-line expressions may extract specific values with subscripts or slices, or any part may be passed to library functions as desired. If omitted, a zero-length args tuple will still be present.

Process a dataset into a new dataset, using “pseudo-SQL”, with jython expressions for each column to return, and with optional jython expressions in Where, Group By, Pivot, Having, Order By, and Limit clauses. Extra arguments are converted into an args tuple for efficient use within the jython expressions.

Available in all scopes.

In Gateway scopes, the returned dataset is the type returned by system.dataset.toDataSet(), typically a BasicDataset. In Vision Client scope and in Designer scope for Vision bindings, the returned dataset is passed through the toTransient() function automatically. This can be reverted by wrapping the view() function in the nonTransient() function, though this is likely not a good idea in these scopes.

Pseudo SQL

The general form of the selectString argument is as follows:

Select
  pyExpression,
  pyExpression As ColumnName,
  pyExpression As "Column Name" ...
Where pyConditionalExpression
Group By pyExpression, pyExpression ...
Pivot pyExpression For pyStringExpression [In pySequenceExpression]
Having pyConditionalExpression
Order By pyExpression, pyExpression ...
Limit pyIntegerExpression

Within the selectString, the clause keywords are not case sensitive, but everything else is case-sensitive. Whitespace and line breaks outside of quotes collapse to single spaces, so the Pseudo-SQL may be formatted for easy readability. How the column data and intermediate values are used within the pyExpressions varies by clause, as follows:

  • Column expressions that include an AS clause are processed verbatim. When no AS clause is present, and the pyExpression is a simple munged source column name, the original column name is used as an implied output column name. In that case, if there is also a Group By and/or Pivot clause, a [0] subscript is appended to the expression.

  • When a column expression has no AS clause, and the expression is not a simple munged column name, the expression is used verbatim. The output column name is then constructed by passing the entire expression through the name munging function.

  • When a column expression is simply an asterisk, it is replaced with the complete source dataset column list, with original output column names, and with [0] subscripts as above when grouping or pivoting.

  • The code extracts the values from each row of the source dataset, assigning them to local variables using the munged column names. If a Where clause is present, it is evaluated immediately, and the row discarded if not True. The Where clause may use the munged names of the source columns, objects normally present in the local scope (system.*, etc.), the args and binding objects as described for the objectScript() expression function, and _r, the row index in the source dataset.

  • If a Group By clause is present, for any rows that pass the Where condition, the group key is computed next. These Group By key expressions have access to the same variables as the Where clause. A dictionary keyed by group is created. Each entry is a list of lists of row values for that keying tuple. After all source rows are collected into groups, the code iterates through the list of group keys, assigning the lists of row values back into the munged source column names.

  • If a Pivot clause is present, the column name string expression in the For subclause is used as a final grouping key, but just for the pivoted column expression. The optional In subclause may be either a python list or a python tuple. If a tuple, only the given column headings will be used. Rows generating other column names will be discarded. But if a list, all missing entries will be added after the given ones and no rows will be discarded. The pivot value expressions are computed before the other selected output columns, with values from just the inner group.

  • The code next computes the output column values using the column expressions in the Select clause. If no Group By or Pivot clause was used, these column expressions may use anything valid in the Where clause. If Group By or Pivot was used, the column expressions must handle the source columns as python lists instead, and may not use _r. In either case, each computed output value is available to following column expressions using its output column name (munged if necessary).

  • If a Having clause is present, it is evaluated immediately after the output values are constructed. It may use any variable syntax valid in an output column expression, including the munged output column names and munged pivot column names. If the Having clause is true, or not given at all, an output row is constructed from each set of output column values.

  • If an Order By clause is present, it is evaluated with each constructed output row to produce an ordering tuple. Like the Having clause, these expressions may use any variables valid in an output column expression. The output rows are collected in a temporary list with the ordering tuples, sorted, then the final row list is extracted from the temporary list. If the Order By clause is omitted, the constructed output rows are added to the final list directly.

If a Limit clause is present, it is evaluated immediately after the output values are sorted and the otherwise final list of output rows generated. The expression must yield an integer, and will take effect only if positive. The list of rows is available as the variable _rows, making it possible to return a percentage of the rows like so:

LIMIT int(0.10 * len(_rows))

Usage Notes

If you need to programmatically create a selectString for this function, you can get munged column names from your actual column names with the system.dataset.mungeColumnName() auxiliary function. Basically, column names that contain non-alphanumeric characters are converted to alphanumeric only, using underscores.

To make grouping operations as similar as practical to real SQL syntax, common aggregate functions are defined in script.aggregate.*. These functions ignore null/None values as is expected in SQL. Since some have names matching python builtins, they are only imported without prefix within the local scope of the view() function. If you use these in a script module called from view(), you must import them or use the full names.

The aggregate functions are not syntactically identical to real SQL, though, as they can only take a numberList argument. In real SQL, a user may place a scalar expression inside the function’s parenthesis, and the SQL language takes care of the list conversion. In view()’s Pseudo-SQL, the user must handle that themselves. Also, the use of an aggregate function in real SQL will deliver an aggregated result, even if no Group By clause is present. In this Pseudo-SQL, you must use Group By 0 to trigger grouping of all rows. (Group By 0 is implied if Pivot is used without Group By.)

Unlike grouping in real SQL, you may not use an output column expression that matches a Group By column or expression. Automation Professionals recommends using [0] or [-1] as a subscript to obtain the first or last row in a group for those expressions. (This is done automatically for simple munged column names.)

When in doubt how view() is handling your code and data, set its logger to DEBUG, or wrap in the debugMe() expression function. This will show you the python whenever it re-compiles, along with execution times. Looking at the generated python will also show other variables you may use in your expressions.

mungeColumnName()

This function takes an arbitrary string and produces a string that is a valid jython identifier. The view() expression functiona delegates to this function when a column name is not itself a valid jython identifier, and when a jython expression is supplied in view()’s Select clause without an As clause.

Sequences of invalid characters are replaced with single underscore characters to yield a valid identifier.

Available as both an expression function (ideal for making mapping object keys acceptable to Perspective) and a scripting function.

mungeColumnName(name) returns String

system.dataset.mungeColumnName(name) returns String

name Any arbitrary string to be munged into a valid identifier.

Available in all scopes.

system.aggregate.*()

system.aggregate.X(numberList) returns Double

X One of count, groupConcat, max, mean, min, popStdDev, stdDev`, or sum. Functionally identical to the native expression functions of the same names.
numberList A list (sequence) of values to be operated on, with nulls dropped. If given an empty list, or empty but for nulls, the aggregate function return value will be None.

These scripting functions supplement and provide alternatives to the native python statistical functions, ignoring nulls in lists in the SQL fashion, rather than failing, or counting them as zeros.

They are imported with a wildcard into the local scope of every execution of the view() expression function, so that Group By operations can have more natural SQL-like aggregate behaviors than the same-named jython built-ins provide.

unQualify()

Given an object, traverse its collections, if any, deep copying to extract values from qualified values at any depth. Intended to simplify Perspective lists, mappings, lists of mappings, et cetera, when passed as an argument to runScript() or objectScript().

Mappings are turned into Python dictionaries. The keys are also deep copied. The keys encountered must yield valid hashables.

Iterables are copied into ArrayLists.

unQualify(object) returns Object

system.util.unQualify(object) returns PyObject

object Any object. Mappings, lists, or arrays will be processed recursively to not have any QualifiedValue objects nested within.

orderedCopy()

Given an object, traverse its collections, if any, deep copying to extract values from qualified values at any depth. Intended to simplify Perspective lists, mappings, lists of mappings, et cetera, when passed as an argument to runScript() or objectScript().

Mappings are turned into ordered Python dictionaries with stringified keys.

Iterables are reordered by the stringified content of the value indicated by the given list key, if the list key is not null/None. The list key will be “name” if omitted.

Both ordering operations use a non-case-sensitive AlphaNumeric “human-friendly” algorithm.

Iterables that are not entirely dictionaries, or do not have any any inner keys for the list key, will be deep copied in their original order.

orderedCopy(object [, listKey]) returns Object

system.util.orderedCopy(object [, listKey]) returns PyObject

object Any object. Mappings, lists, or arrays will be processed recursively to not have any QualifiedValue objects nested within.
listKey Any string, or java null/python None. If omitted, "name" is used.

alNumCompare()

Supplies an AlphaNumeric Comparator, functionally similar to JideSoft’s AlphaNumeric comparator, but with two critical additional features:

  • Implements Serializable, so can be used in objects exchanged by RPC or traversing the Gateway Network.

  • Accepts any java objects in comparisons and stringifies as needed.

system.util.alNumCompare(cased) returns Comparable<?>

cased Boolean. Indicates whether to the comparator should be case-sensitive or not. Supply `True` for the case-sensitive version.

VarMap

This class, exposed as system.util.VarMap, is the basis for the BindableVarMap objects below. As an implementation of Java’s ConcurrentHashMap<String, Object>, it is automatically wrapped by Jython with all of the ordinary dictionary methods and properties, while retaining its Java methods. But its Jython wrapper is further enhanced to divert attribute access to dictionary keys (excluding writes to attributes that start with an underscore). And attribute reads that match no dictionary key return None instead of raising an AttributeError.

This class is intended to supercede the shared.util.SmartMap class that Automation Professionals has published in various places. That class was inspired by Peter Norvig’s “bag of properties” recommendations in his Python Infrequently Asked Questions.

This implementation has the obvious performance advantages of pure Java, but is also safe to place in persistent JVM locations, including in the globalVarMap() below, just like native Jython dictionaries and other native Jython objects. It is also serializable, as long as all of its contents are serializable, so can be sent back and forth between Gateway, Designer, and Vision Client scopes freely.

Since it isn’t defined in a project library script, it is safe to import for an abbreviated form:

from system.util import VarMap
# or
from com.automation_pros.simaids.persist import VarMap

globalVarMap()

This expression function and the matching scripting function (as system.util.globalVarMap()) return a unique, persistent mapping object associated with a given string key. Once created, the mapping object will exist until the JVM exits. There is no provision for deleting one of these mapping objects once created. (Though they can be cleared.) There is no provision for enumerating existing keys (deliberately). The string key should be a constant associated with the specific application purpose. Warning! Misuse of these functions will leak memory and can crash your system.

globalVarMap(key) returns BindableVarMap in an expression.

system.util.globalVarMap(key) returns BindableVarMap in a script.

key A constant string uniquely identifying a JVM global dictionary-like object.

This function is available in all scopes.

The BindableVarMap return value is a subclass VarMap, described above, which gives it all of the methods of a ConcurrentHashMap along with all of a Jython dictionary’s methods and the attribute access to the dictionary content. It has these further enhancements:

  • Can weakly attach to any InteractionListener via its .bind() method. This method is automatically invoked by the expression function when in a context that provides one.

  • Can trigger any InteractionListener that is attached (and has not been garbage collected) via its .refresh() method. That is, a script that retrieves one of these BindableVarMap instances can cause expressions that use the same key to re-evaluate. Something like this:

bvm = system.util.globalVarMap("someKey")
bvm.someAttr = "new value to distribute"
bvm['some non-identifier key'] = "some other new value"
bvm.refresh()

Side note: this functionality is intended to replace Automation Professionals’ system.util.persistent() scripting function that has been available in the “Life Cycle” module for Ignition v7.9 and beyond. That module was created to solve some problems with system.util.getGlobals() introduced in the middle of the v7.9 series and continuing into v8+, but also to avoid the overloaded use of system.util.getGlobals() for certain legacy-scoped gateway events. globalVarMap() avoids all of those problems and adds attribute handling and binding to the mix.

sessionVarMap()

This expression function and the various overloaded scripting functions (as system.perspective.sessionVarMap(...)) return a unique mapping object associated with a Perspective session. The mapping object destroys itself when the associated Perspective session is garbage collected. Like globalVarMap, the refresh() method may be used from jython to update any UI bindings attached to the mapping object.

sessionVarMap() returns BindableVarMap in an expression.

system.perspective.sessionVarMap() returns BindableVarMap in a script.

Returns the mapping object for the current session.

Available only in Perspective scope.

system.perspective.sessionVarMap(reference) returns BindableVarMap in a script.

reference Can be a Perspective session object, a Perspective page or view object from a session, or the UUID of a Perspective session.

Returns the mapping object attached to or associated with the session of the given reference.

Available in Perspective and Gateway scopes.

pageVarMap()

This expression function and the various overloaded scripting functions (as system.perspective.pageVarMap(...)) return a unique mapping object associated with a Perspective page, except in the designer, where it returns the sessionVarMap. The mapping object destroys itself when the associated Perspective object is garbage collected. Like globalVarMap, the refresh() method may be used from jython to update any UI bindings attached to the mapping object.

The designer environment doesn’t allow navigation, and treats each open view as if in a page by itself. This would make it impossible to test pageVarMap() using views that are supposed to live together in a page, except that this function falls back to the sessionVarMap() in a designer session. To avoid clashes in the designer, use different dictionary keys in pages versus sessions.

pageVarMap() returns BindableVarMap in an expression.

system.perspective.pageVarMap() returns BindableVarMap in a script.

Returns the mapping object for the current page or designer session.

Available only in Perspective Page or View scope.

system.perspective.pageVarMap(pageId) returns BindableVarMap in a script.

pageId A string page ID that identifies a specific page scope in the current Perspective session.

Returns the mapping object attached to or associated with the specified page or designer session.

Available in Perspective scope.

system.perspective.pageVarMap(reference) returns BindableVarMap in a script.

reference Can be a Perspective page or view object from a session.

Returns the mapping object attached to or associated with the page or designer session of the given reference.

Available in Perspective and Gateway scopes.

system.perspective.pageVarMap(reference, pageId) returns BindableVarMap in a script.

reference Can be a Perspective session object, a Perspective page or view object from a session, or the UUID of a Perspective session.
pageId A string page ID that identifies a specific page scope in the referenced Perspective session.

Returns the mapping object attached to or associated with the specified page or designer session of the session identified by the given reference.

Available in Perspective and Gateway scopes.

viewVarMap()

This expression function and the various overloaded scripting functions (as system.perspective.viewVarMap(...)) return a unique mapping object associated with a Perspective view. The mapping object destroys itself when the associated Perspective view is garbage collected. Like globalVarMap, the refresh() method may be used from jython to update any UI bindings attached to the mapping object.

viewVarMap() returns BindableVarMap in an expression.

system.perspective.viewVarMap() returns BindableVarMap in a script.

Returns the mapping object for the current view.

Available only in Perspective View scope.

system.perspective.viewVarMap(viewId) returns BindableVarMap in a script.

viewId A string view ID that identifies a specific view scope in the current Perspective session and page.

Returns the mapping object attached to or associated with the specified view in the current page.

Available in Perspective Page or View scope.

system.perspective.viewVarMap(pageId, viewId) returns BindableVarMap in a script.

pageId A string page ID that identifies a specific page scope in the current Perspective session.
viewId A string view ID that identifies a specific view scope in the current Perspective session and specifiedpage.

Returns the mapping object attached to or associated with the specified view.

Available in Perspective scope.

system.perspective.viewVarMap(reference) returns BindableVarMap in a script.

reference Can be a Perspective view or view-linked object from a session.

Returns the mapping object attached to or associated with the view of the given reference.

Available in Perspective and Gateway scopes.

system.perspective.viewVarMap(reference, viewId) returns BindableVarMap in a script.

reference Can be a Perspective page or view or view-linked object from a session.
viewId A string view ID that identifies a specific view scope in the referenced Perspective session and page.

Returns the mapping object attached to or associated with the specified view of the page identified by the given reference.

Available in Perspective and Gateway scopes.

system.perspective.viewVarMap(reference, pageId, viewId) returns BindableVarMap in a script.

reference Can be a Perspective page or view or view-linked object from a session.
pageId A string page ID that identifies a specific page scope in the referenced Perspective session.
viewId A string view ID that identifies a specific view scope in the referenced Perspective session and page.

Returns the mapping object attached to or associated with the specified view of the session identified by the given reference and specified page of the session.

Available in Perspective and Gateway scopes.