Running "Rollback" scripts on View onShutdown (nav or logout) event

I have a view in Perspective where operators test parts, and based on test results assign a “Complete” (good), “Blemish”, or “Scrap” status to the part being tested. We have added the ability to edit parts that have already been tested. As part of this, a “reprocess” ability has been added.

The original testing functionality (still in place) updates a DB record with weight first, then micrometer measurements are taken and calculations for volume, density, etc. are made and the record is then updated again. Next a barcode label string is created and printed on the part. That barcode is then posted to the record. Finally the operator “marks” the part as Complete, Blemish, Scrap and the record is again updated.

So the parts record is updated multiple times during the testing process.
This is also the case during reprocessing as the same code has been used.

During initial testing of the parts, if the operator is logged out due to inactivity or navigates away from the View in the middle of the process, the part’s record is simply deleted. We can’t do this for already processed parts, but we do need to rollback the updates to the part’s record.

I have message handlers in the view and project scripts to do this.
My issue is that if I put calls to the message handers, which then call the scripts, passing parameters from the View’s root, the message handlers/scripts are not getting executed as expected.
I am getting log messages like this:

script	21Apr2022 10:20:57	Error running action 'system.onShutdown' on embedded/inspection_process@C$0:2: java.lang.IllegalStateException: java.lang.IllegalStateException: Session is not running.
com.inductiveautomation.ignition.common.script.JythonExecException: java.lang.IllegalStateException: java.lang.IllegalStateException: Session is not running.
at org.python.core.Py.JavaError(Py.java:547)
at org.python.core.Py.JavaError(Py.java:538)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:192)
at com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction.__call__(ScriptManager.java:546)
at org.python.core.PyObject.__call__(PyObject.java:400)
at org.python.pycode._pyx834.runAction$1(:56)
at org.python.pycode._pyx834.call_function()
at org.python.core.PyTableCode.call(PyTableCode.java:173)
at org.python.core.PyBaseCode.call(PyBaseCode.java:306)
at org.python.core.PyFunction.function___call__(PyFunction.java:474)
at org.python.core.PyFunction.__call__(PyFunction.java:469)
at org.python.core.PyFunction.__call__(PyFunction.java:464)
at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:849)
at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:831)
at com.inductiveautomation.ignition.gateway.project.ProjectScriptLifecycle$TrackingProjectScriptManager.runFunction(ProjectScriptLifecycle.java:689)
at com.inductiveautomation.ignition.common.script.ScriptManager$ScriptFunctionImpl.invoke(ScriptManager.java:1000)
at com.inductiveautomation.ignition.gateway.project.ProjectScriptLifecycle$AutoRecompilingScriptFunction.invoke(ProjectScriptLifecycle.java:754)
at com.inductiveautomation.perspective.gateway.script.ScriptFunctionHelper.invoke(ScriptFunctionHelper.java:133)
at com.inductiveautomation.perspective.gateway.action.ScriptAction.runAction(ScriptAction.java:71)
at com.inductiveautomation.perspective.gateway.action.ActionDecorator.runAction(ActionDecorator.java:18)
at com.inductiveautomation.perspective.gateway.action.SecuredAction.runAction(SecuredAction.java:44)
at com.inductiveautomation.perspective.gateway.model.ActionCollection$ActionSequence$ExecuteActionsTask.lambda$call$0(ActionCollection.java:263)
at com.inductiveautomation.perspective.gateway.api.LoggingContext.mdc(LoggingContext.java:54)
at com.inductiveautomation.perspective.gateway.model.ActionCollection$ActionSequence$ExecuteActionsTask.call(ActionCollection.java:252)
at com.inductiveautomation.perspective.gateway.model.ActionCollection$ActionSequence$ExecuteActionsTask.call(ActionCollection.java:221)
at com.inductiveautomation.perspective.gateway.threading.BlockingTaskQueue$TaskWrapper.run(BlockingTaskQueue.java:154)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.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: org.python.core.PyException: java.lang.IllegalStateException: java.lang.IllegalStateException: Session is not running.
... 31 common frames omitted
Caused by: java.lang.IllegalStateException: Session is not running.
at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.getSession(AbstractScriptingFunctions.java:111)
at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnSession(AbstractScriptingFunctions.java:118)
at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.sendMessage(PerspectiveScriptingFunctions.java:551)
at jdk.internal.reflect.GeneratedMethodAccessor77.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)
... 28 common frames omitted

I’m guessing that as the view is shutting down, components in the view (like the message handler, parameters, etc.) are being purged and no longer exist when the onShutdown Event Script tries to access them.

What is the best way to perform an event to do something like this without having to completely rewrite the testing procedure? I think it should be able to be done via the onShutdown event, but maybe I’m mistaken on that.

Thanks.

Hey Mike,

One option would be to store the ‘update’ data into a session/custom property, and only update the DB once the testing/reprocessing is complete. That way you won’t have to worry about retroactively rolling back data.

Another option would be to disable navigation until the end of the process. You might also want to look into using custom session props to hold data that you were previously trying to access from message handlers.

1 Like

Hey Cosmo,
My original solution started by storing all the initial data values to view parameters. It also employed message handlers in the view (that was getting the onShutdown event triggered via logout or nav events) to rollback using said data.
Apparently the message handlers in the view, were, by the time they were called, no longer accessible to the onShutdown event code. I figure this to be the case because I added logging to all the individual code parts to see what the actual flow of logic was, and none of the loggers in the message handlers posted any entries to the log.
The fix I finally settled on was to move all the code to Project Library scripts and pass the stored data to the scripts as needed. This works and I have not seen any breakdown in the process using it.

Thanks for the input!