Show a popup in perspective on all clients if a value changes

In perspective I have a popup page that I want to show up on all screens in the network if a certain tag is set to true. How would I go about doing that and what would that script look like?

thank you

Bind the tag to a session custom property and use a change script on the property to open the popup.

1 Like

what would that change script include? would I use an If statement followed by system.perpective.openPopup? I am pretty new to ignition

@mcgheeiv has supplied one route, but the problem is that session properties have no insight into the page in use, which is required when opening a Popup. Your logs would supply an exception or error along the lines of “No perspective page attached to thread.”

There are some important things to consider when planning this: Each browser session could have multiple pages open, so which page gets the Popup? The first page found for a session, or all pages? What conditions would warrant closing the popup across across any pages it is opened on? Would sessions opened AFTER the opening condition becomes true instantly open the popup, or would they be exempt?

I’m going to make some assumptions, and you can manipulate the logic as you see fit. Let’s assume this is a boolean tag, and you want the Popup to be opened for all pages in all sessions when the tag becomes true, and you want the Popup to close for all pages in all sessions when the tag becomes false. Pages/Sessions opened after the tag has become true will be exempt from the Popup.

I think the best solution is going to be to have tag change script.

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	popupId = "SomeUniqueValue"
	viewPath = "PopupViews/SmallPopup"  # insert your view path here
	logger = system.util.getLogger("POPUP")
	sessions = system.perspective.getSessionInfo()
	for session in sessions:
		for pageId in session.pageIds:
			logger.info("{0} | {1}".format(session.id, pageId))
			if currentValue.value:
				system.perspective.openPopup(popupId, viewPath, sessionId=session.id, pageId=pageId)
			else:
				# note no viewPath arg
				system.perspective.closePopup(popupId, sessionId=session.id, pageId=pageId)
5 Likes

It should be pretty simple… If the new value is true open popup…

https://docs.inductiveautomation.com/display/DOC81/system.perspective.openPopup

This is pretty much exactly what I am trying to do. Should this be a gateway event tag change script? in the logic is there anything else I need to substitute in beside viewPath? thank you for all of the help, like I said pretty new to ignition so still some things I need to learn.

No, this should be in the value changed script for a tag.

You should replace the “SomeUniqueValue” with something which is either a UUID or which holds a specific meaning to your use-case; that ID is used to open/close the popup across all sessions, and so if you use the same ID for any other popup you risk trampling other usages.

One question: what happens if there are multiple runnable projects and this only applies for one project?
Another: you’re also not filtering by session type, I.e. Filtering out designer sessions. Probably not an issue since the designers would just ignore it anyway, does it produce an error though?

system.perspective.getSessionInfo() allows for filtering sessions by username or project, so you could always just get sessions for project "X".

Designer sessions ignore the request for the popup, just like they ignore attempts at navigation.

2 Likes

Would it not be possible to use a message handler here? That way filtering pages is as simple as adding the message handler or not if there are pages in the future you want to exempt.

Why do I use a tag change scrip on the tag vs the gateway? What is the difference between using one or the other?

INFO | jvm 1 | 2022/06/27 08:57:24 | Current value is:
INFO | jvm 1 | 2022/06/27 08:57:24 | True
INFO | jvm 1 | 2022/06/27 08:57:24 | O2Alarm
INFO | jvm 1 | 2022/06/27 08:57:24 | Page/Popups/O2 Alarm Popup
INFO | jvm 1 | 2022/06/27 08:57:24 | I [POPUP ] [13:57:24]: efdfcba8-5006-464c-a1a3-56a33a88059e | 172301c6
INFO | jvm 1 | 2022/06/27 08:57:24 | I [POPUP ] [13:57:24]: E066391F | Page/Popups/O2 Alarm Popup
INFO | jvm 1 | 2022/06/27 08:57:28 | I [c.s.p.g.ProductionGatewayHook ] [13:57:28]: Unable to check availability of null datasource (Local).
INFO | jvm 1 | 2022/06/27 08:57:30 | I [POPUP ] [13:57:29]: efdfcba8-5006-464c-a1a3-56a33a88059e | 172301c6
INFO | jvm 1 | 2022/06/27 08:57:30 | I [POPUP ] [13:57:29]: E066391F | Page/Popups/O2 Alarm Popup

This is what is showing up in my wrapper logs, added a few print statements inside the if statement which show up there but still not popping up on my sessions.

popupId = "O2Alarm"
viewPath = "Page/Popups/O2 Alarm Popup"  # insert your view path here
logger = system.util.getLogger("POPUP")
sessions = system.perspective.getSessionInfo()
for session in sessions:
	for pageId in session.pageIds:
		logger.info("{0} | {1}".format(session.id, pageId))

		if currentValue.value:
			print "Current value is:"
			print currentValue.value
			print popupId
			print viewPath
			system.perspective.openPopup(popupId, viewPath, sessionId=session.id, pageId=pageId)
		else:
			# note no viewPath arg
			system.perspective.closePopup(popupId, sessionId=session.id, pageId=pageId)

This is what I have for my code which is pretty much what you sent besides a few added print statements, not quite sure why my popup is not showing up.

That logging looks very much like it should be working, and I even see a sessionId and pageId in the logging. That code worked for me and I’ve even verified that the space in the View path does not affect functionality. Are you positive you saved after creating the “Page/Popups/O2 Alarm Popup” file?

I am positive that I had saved that file, everything else seems as it is working, strictly the popup is not showing up when enabling that tag.

Remove the logging you put into place and place a new line of logging AFTER the popup functions are called. This will tell you whether or not the function is executing as expected. Are you looking at the browser session, or are you testing this in the Designer?

Looking at it in the browser session and still have no popup, have tried changing value in designer as well as in studio 5000 to toggle the value.

Below is what happens in the logger when I place logger.info(“{0} | {1}” … inside of the if statement

INFO | jvm 1 | 2022/06/27 10:33:29 | Current value is:
INFO | jvm 1 | 2022/06/27 10:33:29 | True
INFO | jvm 1 | 2022/06/27 10:33:29 | O2Alarm
INFO | jvm 1 | 2022/06/27 10:33:29 | Page/Popups/O2 Alarm Popup
INFO | jvm 1 | 2022/06/27 10:33:29 | Current value is:
INFO | jvm 1 | 2022/06/27 10:33:29 | True
INFO | jvm 1 | 2022/06/27 10:33:29 | I [POPUP ] [15:33:29]: efdfcba8-5006-464c-a1a3-56a33a88059e | 172301c6
INFO | jvm 1 | 2022/06/27 10:33:29 | O2Alarm
INFO | jvm 1 | 2022/06/27 10:33:29 | Page/Popups/O2 Alarm Popup
INFO | jvm 1 | 2022/06/27 10:33:29 | I [POPUP ] [15:33:29]: E066391F | Page/Popups/O2 Alarm Popup
INFO | jvm 1 | 2022/06/27 10:33:29 | E [t.e.dispatcher ] [15:33:29]: ([default]OKC/O2/O2_Popup_Trigger, valueChanged) Error executing tag event script: Traceback (most recent call last):
INFO | jvm 1 | 2022/06/27 10:33:29 | File “tagevent:valueChanged”, line 16, in valueChanged
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.UUID.fromString(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.getSession(AbstractScriptingFunctions.java:91)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnSession(AbstractScriptingFunctions.java:118)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnPage(AbstractScriptingFunctions.java:47)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.popupAction(PerspectiveScriptingFunctions.java:616)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.openPopup(PerspectiveScriptingFunctions.java:231)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.lang.reflect.Method.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid UUID string: E066391F
INFO | jvm 1 | 2022/06/27 10:33:29 |
INFO | jvm 1 | 2022/06/27 10:33:29 | com.inductiveautomation.ignition.common.script.JythonExecException: Traceback (most recent call last):
INFO | jvm 1 | 2022/06/27 10:33:29 | File “tagevent:valueChanged”, line 16, in valueChanged
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.UUID.fromString(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.getSession(AbstractScriptingFunctions.java:91)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnSession(AbstractScriptingFunctions.java:118)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnPage(AbstractScriptingFunctions.java:47)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.popupAction(PerspectiveScriptingFunctions.java:616)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.openPopup(PerspectiveScriptingFunctions.java:231)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.lang.reflect.Method.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid UUID string: E066391F
INFO | jvm 1 | 2022/06/27 10:33:29 |
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.Py.JavaError(Py.java:547)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.Py.JavaError(Py.java:538)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyReflectedFunction.call(PyReflectedFunction.java:192)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction.call(ScriptManager.java:541)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyObject.call(PyObject.java:400)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.pycode.pyx34.valueChanged$1(tagevent:valueChanged:8)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.pycode.pyx34.call_function(tagevent:valueChanged)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyTableCode.call(PyTableCode.java:173)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyBaseCode.call(PyBaseCode.java:306)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyFunction.function___call
(PyFunction.java:474)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyFunction.call(PyFunction.java:469)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyFunction.call(PyFunction.java:464)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:831)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:813)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.gateway.tags.scripting.TagScriptManagerImpl$FunctionInvokerImpl.run(TagScriptManagerImpl.java:533)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.gateway.tags.scripting.events.AbstractTagScript.invoke(AbstractTagScript.java:34)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.gateway.tags.scripting.TagScriptManagerImpl$Task.invoke(TagScriptManagerImpl.java:482)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.gateway.tags.scripting.TagScriptManagerImpl$TagScriptDispatcher.run(TagScriptManagerImpl.java:445)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.ignition.common.execution.impl.BasicExecutionEngine$ThrowableCatchingRunnable.run(BasicExecutionEngine.java:539)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.lang.Thread.run(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | Caused by: org.python.core.PyException: java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid UUID string: E066391F
INFO | jvm 1 | 2022/06/27 10:33:29 | … 25 common frames omitted
INFO | jvm 1 | 2022/06/27 10:33:29 | Caused by: java.lang.IllegalArgumentException: Invalid UUID string: E066391F
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.util.UUID.fromString(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.getSession(AbstractScriptingFunctions.java:91)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnSession(AbstractScriptingFunctions.java:118)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnPage(AbstractScriptingFunctions.java:47)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.popupAction(PerspectiveScriptingFunctions.java:616)
INFO | jvm 1 | 2022/06/27 10:33:29 | at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.openPopup(PerspectiveScriptingFunctions.java:231)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at java.base/java.lang.reflect.Method.invoke(Unknown Source)
INFO | jvm 1 | 2022/06/27 10:33:29 | at org.python.core.PyReflectedFunction.call(PyReflectedFunction.java:190)
INFO | jvm 1 | 2022/06/27 10:33:29 | … 22 common frames omitted
INFO | jvm 1 | 2022/06/27 10:33:32 | I [c.s.p.g.ProductionGatewayHook ] [15:33:32]: Unable to check availability of null datasource (Local).
INFO | jvm 1 | 2022/06/27 10:33:47 | I [c.s.p.g.ProductionGatewayHook ] [15:33:47]: Unable to check availability of null datasource (Local).
INFO | jvm 1 | 2022/06/27 10:34:02 | I [c.s.p.g.ProductionGatewayHook ] [15:34:02]: Unable to check availability of null datasource (Local).

No, don’t place the same logging, just put

logger.info("openPopup complete")

on the line following the openPopup function. I recommend removing your print statements, just to remove clutter. Also, always place any stacktraces within a code block (triple back-ticks) so that it can be easily parsed.

Also also, if you modify code and provide a new stacktrace, you should also provide the NEW code in a code-block, and it’s extra helpful if you can identify which line the error is referencing. In your case, leave a comment on what is line 16 in your code-block. If you don’t do this, then others trying to help you have to make guesses about which line you’ve made changes to, and what could possibly be triggering the error.

When I run the following code:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	popupId = "O2Alarm"
	viewPath = "Split"  # insert your view path here
	logger = system.util.getLogger("POPUP")
	sessions = system.perspective.getSessionInfo()
	for session in sessions:
		for pageId in session.pageIds:
			logger.info("{0} | {1}".format(session.id, pageId))
	
			if currentValue and currentValue.value:  # I added an additional safety check for first time use here
				system.perspective.openPopup(popupId, viewPath, sessionId=session.id, pageId=pageId)
				logger.info("openPopup complete")
			else:
				# note no viewPath arg
				system.perspective.closePopup(popupId, sessionId=session.id, pageId=pageId)

I get the following logging:

POPUP	27Jun2022 08:49:51	B9B1743A | Split
POPUP	27Jun2022 08:49:51	openPopup complete
POPUP	27Jun2022 08:49:51	f456b99d-af9d-4410-94ce-39aec47272c8 | 32a9092e

Which tells me that

  1. the first page found was a browser session
  2. the popup function executed as expected (and I verified a Popup was open)
  3. A designer “page” was found (note the 8-digit identifier in use for the sessionId and View name in use for the pageId).
  4. The openPopup function did not complete the second time through because the session was a Designer.

Also, my line 14 was my previous line 16, I just removed white space from above.

This is my wrapper log

This is my code
I am still not getting a popup in the browser session.

I am using the tag path from my designer and copying it from there, I assume that’s what I am supposed to use?

image

This is the path I am using