Automation Professionals' Integration Toolkit Module

Here you go:

ToolkitRPCImpl

03Feb2026 14:09:46
Unexpected error delegating to toolkitTest.gwLogInfof
java.util.NoSuchElementException: No value present

at java.base/java.util.Optional.orElseThrow(Unknown Source)

at com.automation_pros.simaids.gateway.ToolkitRPCImpl.runInGateway(ToolkitRPCImpl.java:77)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

at java.base/java.lang.reflect.Method.invoke(Unknown Source)

at com.inductiveautomation.ignition.gateway.rpc.RpcDelegate$DelegateRpcHandler.handle(RpcDelegate.java:275)

at com.inductiveautomation.ignition.gateway.rpc.RpcRoutes.lambda$handle$1(RpcRoutes.java:209)

at com.inductiveautomation.ignition.gateway.rpc.RpcRoutes.safelyHandle(RpcRoutes.java:225)

at com.inductiveautomation.ignition.gateway.rpc.RpcRoutes.handle(RpcRoutes.java:209)

at com.inductiveautomation.ignition.gateway.dataroutes.Route.service(Route.java:355)

at com.inductiveautomation.ignition.gateway.dataroutes.RouteGroupImpl.service(RouteGroupImpl.java:117)

at com.inductiveautomation.ignition.gateway.dataroutes.RouteGroupCollectionServlet.serviceInternal(RouteGroupCollectionServlet.java:152)

at com.inductiveautomation.ignition.gateway.dataroutes.AbstractRouteGroupServlet.service(AbstractRouteGroupServlet.java:40)

at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)

at org.eclipse.jetty.ee10.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1385)

at org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:751)

at org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1622)

at com.inductiveautomation.catapult.filters.GatewayFilter.doFilter(GatewayFilter.java:119)

at jakarta.servlet.http.HttpFilter.doFilter(HttpFilter.java:97)

at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)

at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)

at org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1555)

at org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:823)

at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:440)

at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:470)

at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:575)

at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:717)

at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1071)

at org.eclipse.jetty.rewrite.handler.RewriteHandler$LastRuleHandler.handle(RewriteHandler.java:159)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:143)

at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:805)

at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:805)

at org.eclipse.jetty.server.Server.handle(Server.java:182)

at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:677)

at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:416)

at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)

at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)

at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:480)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:443)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:293)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:201)

at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:311)

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:981)

at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1211)

at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1166)

at java.base/java.lang.Thread.run(Unknown Source)

Huh. Not helpful. And seems I never properly published v2.1.2... Coming up.

For Ignition v8.3+: v2.1.2.253151852

I found this in December, and queued it up, but didn't push. ):

(v2.1.1 doesn't have the ClientReqSession material at all.)

1 Like

My example works with v2.1.2 :+1:
Thanks!

1 Like

Is there room for improvement with how lists are coerced back into PyObjects?

# Script Module (toolkitTest)
@system.util.runInGateway
def testReturnList():
	myList = []
	return myList

# Script Console
toolkitTest.testReturnList()
Java Traceback:
Traceback (most recent call last):
  File "<input>", line 3, in <module>
java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at jdk.proxy2/jdk.proxy2.$Proxy86.runInGateway(Unknown Source)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__gateway_call__(RunInGateway.java:51)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__call__(RunInGateway.java:41)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__call__(RunInGateway.java:56)

	at org.python.pycode._pyx65.f$0(<input>:3)

	at org.python.pycode._pyx65.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1703)

	at org.python.core.Py.exec(Py.java:1747)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:639)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:627)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

java.lang.ClassCastException: java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)


	at org.python.core.Py.JavaError(Py.java:545)

	at org.python.core.PyTableCode.call(PyTableCode.java:182)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1703)

	at org.python.core.Py.exec(Py.java:1747)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:639)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:627)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

Caused by: java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at jdk.proxy2/jdk.proxy2.$Proxy86.runInGateway(Unknown Source)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__gateway_call__(RunInGateway.java:51)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__call__(RunInGateway.java:41)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__call__(RunInGateway.java:56)

	at org.python.pycode._pyx65.f$0(<input>:3)

	at org.python.pycode._pyx65.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	... 13 more

Traceback (most recent call last):
  File "<input>", line 3, in <module>
java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at jdk.proxy2/jdk.proxy2.$Proxy86.runInGateway(Unknown Source)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__gateway_call__(RunInGateway.java:51)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__call__(RunInGateway.java:41)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction.__call__(RunInGateway.java:56)

	at org.python.pycode._pyx65.f$0(<input>:3)

	at org.python.pycode._pyx65.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1703)

	at org.python.core.Py.exec(Py.java:1747)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:639)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:627)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

java.lang.ClassCastException: java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

The inevitable pain of (de)serialization is part of the reason I have been so hesitant to do something like this. Looks like Phil's using the base proto based RPC serialization in 8.3, which hands back real (unmodifiable) List instances when given one over RPC, instead of any wrapped Jython thing.

Will investigate.

1 Like

Automation Professionals is pleased to announce new Production Releases of this module with the following new utility functions:

  • system.util.runInClient()
  • rex()
  • linesOf()
  • parseJTime()
  • formatJTime()

For Ignition v8.3: v2.1.4.260821836

For Ignition v8.1: v2.1.4.260821551

I do not have a built-in solution to Ben's list type issue, but I suspect it would be mitigated if the target function for runInGateway used my system.util.unQualify() function to re-wrap the return value.

Edit: Also, the expression functions that return lists now return instances of PyList instead of ArrayList, to make them more friendly to Document tags and Vision Document properties.

7 Likes

I'm trying to walk through a json using a tag path, and then returning the last found key. I can do this easily in a python script, but I'm trying to do it using an expression.

This is how I'm doing it in python:


def getAlarmNode(alarmDoc, path):
    parts = path.split('/')
    node = alarmDoc
    for part in parts:
        node = node.get(part)
        if node is None:
            return None
    return node

The alarmDoc is a document tag representing a dictionary of alarms and counts in an area.
IE

{
	'default':{
		'details':{
			'count':0
		},
		'Process':{
			'details':{
				'count':0
			},
			'Furnace':{
				'details':{
					'count':0
				}
			}
		}
	}
}

Basically I'd like to be able to parse a Tag/Folder path that looks like [default]Process or [default]Process/Furnace using an expression rather than a script.

Any pointers as to where to start?

Hmmm. Technically, that's a recursive operation, which I don't support. But I imagine you could hard-code a few levels with coalesce(). Start with my parsePath() function.

That was the route I originally took, but I can't seem to access the path of a document tag via dynamic data. I'll keep playing around though and see if I can make it happen.

Note: This is all happening in a UDT as well, not on the UI layer.