Invalid UUID string on tag change script

Hi, the code below results in an invalid UUID error.

    popupId = "3Ed23lNx"
    viewPath = "Page/Popups/warnings/warningsReminders/warningReminderBp" # insert your view path here
    logger = system.util.getLogger("POPUP")
    sessions = system.perspective.getSessionInfo(projectFilter="Production")
    
    pageParams = {
            'areaName':'',
            'description':'Instrument Air Pressure Low: \n Below 350 KPa',
            'popupID': popupId,
            'title': "Instrument Air Pressure Low Warning"
            }

    for session in sessions:
        for pageId in session.pageIds:
            logger.info("{0} | {1}".format(session.id, pageId))
            if currentValue.value:
                system.perspective.openPopup(id=popupId, view=viewPath, params=pageParams, sessionId=session.id, pageId=pageId)
            else:
                # note no viewPath or param arg
                system.perspective.closePopup(id=popupId, sessionId=session.id, pageId=pageId)

Error Code:

Error executing script.
Traceback (most recent call last):
  File "<tagevent:valueChanged>", line 26, in valueChanged
	at java.base/java.util.UUID.fromString1(Unknown Source)
	at java.base/java.util.UUID.fromString(Unknown Source)
	at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.getSession(AbstractScriptingFunctions.java:91)
	at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnSession(AbstractScriptingFunctions.java:118)
	at com.inductiveautomation.perspective.gateway.script.AbstractScriptingFunctions.operateOnPage(AbstractScriptingFunctions.java:47)
	at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.popupAction(PerspectiveScriptingFunctions.java:758)
	at com.inductiveautomation.perspective.gateway.script.PerspectiveScriptingFunctions.closePopup(PerspectiveScriptingFunctions.java:258)
	at jdk.internal.reflect.GeneratedMethodAccessor550.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid UUID string: D7183798

Any assistance would be appreciated.
Thank you.

Never mind, it turns out that when the users concurrently working within the same project (from their own designers) exceeds 2 users, and the script is changed / implemented while this is the case, then the UUID value becomes invalid.

This is a bug with Ignition.

Ignition's behaviour aside...

A tag event script is basically the last place I would ever expect a GUI manipulation action to be occuring. This seems like a really fragile, hard to debug, hard to maintain pattern.

I would strongly recommend:

  1. Create a session custom property for the value you want to monitor.
  2. Bind it to the tag in question.
  3. Add a change script that fires your Perspective specific logic.

Even if you're using a tag event script so that you can repeat the script easily, there are solutions that follow the above pattern and aren't a maintenance boondoggle.

3 Likes

Hi,

I have to use a timer script because I need the popup alarm warning to appear every 30 000 ms after it has been dismissed, after it is cleared it no longer appears. I tried using a while loop but it did not work.

I did use your method of adding a custom session property, but getting it repeat itself is a challenge. I have to make 10 alarm notification popups with this logic.

Thank you.

A timer script ? The trace you posted seems to reference a tag value change, not a timer script.

As Paul said, tags really should not be in charge of doing anything UI related. They're not in the right scope for this. Try having something that's session scoped, make it "subscribe" to the tag, and deal with things there.
You might want something like a queue, and a gateway timer script that checks that queue periodically to see if there are events it should handle.

I suggest you try to explain what you want to achieve, I'm sure someone here will help you find the proper way to achieve it.

2 Likes

oops :grimacing:

good catch, fixed it.

There is always another way.

  • Create two custom properties on the popup view: startTime and timeout.
  • On startTime create an expression binding now(0).
  • On timeout create an expression binding:
    (now(1000) - {this.custom.startTime}) > 5000 // (5 s for faster testing)
  • Right-click on custom.timeout → Edit Change Script.
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	if currentValue.value:
		system.perspective.closePopup("")

Test. You may need to use the popupId in the closePopup function. (I didn't.)

Hi,

Thank you for the reply. I have done something similar.

image

threshold is the amount to time to elapse before showing the popup again.
timeack is the time at which the user clicked the dismiss button on the alarm popup.
value is bound to the Boolean memory tag, and the scripting is as follows:

def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	title = "Warning"
	popupId = "3Ed23lNx"
	viewPath = "Page/Popups/warnings/warningsReminders/warningReminderBp"  # insert your view path here
	logger = system.util.getLogger("POPUP1")
	sessions = system.perspective.getSessionInfo(projectFilter="Production")
	
	#    now = system.date.now()
	path = ["[default]Popupfaults/Instrument air pressure/Instrument Air Pressure Low", "[default]Popupfaults/Instrument air pressure/air pressure value"]
	values = system.tag.readBlocking(path)
	value = values[0]
	actual = values[1]
	
	#    timeDiff = system.date.secondsBetween(value.timestamp, now)
	#	actual: %s, setpoint %s' %(actual.value, setpoint.value)
	description = 'Instrument air pressure low.<br> Air pressure is below 350 Kilopascals.'
	
	pageParams = {
	        'areaName':'',
	        'description':description,
	        'popupID': popupId,
	        'title': 'Instrument Air Pressure Low Warning',
	        'label1': '',
	        'label2': 'Pressure',
	        'units1':'',
	        'units2':'KPa',
	        'value1':'',
	        'value2':'%.2f'%(actual.value)
	        }
	
	for session in sessions:
	    for pageId in session.pageIds:
	        logger.info("{0} | {1}".format(session.id, pageId))
	        if session.userAgent != "<designer>":
	        	if currentValue.value:
	        		system.perspective.openPopup(id=popupId, view=viewPath, params=pageParams, sessionId=session.id, pageId=pageId, title=title, showCloseIcon=False)
	        	else:
	        		# note no viewPath or param arg
					system.perspective.closePopup(id=popupId, sessionId=session.id, pageId=pageId)

The above script runs when the value is Boolean memory tag first changes.

countdown has the expression:

try({this.custom.instruAirPress.threshold}-{this.custom.instruAirPress.inactivetime},999)

the on change script is

def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	if currentValue.value is not None:
			if currentValue.value <= 0:
				# Reset to stop the countdown, "waiting for the user to click 'ok'
				setattr(self.session.custom, "instruAirPress.timeack", "null")
				
				title = "Warning"
				popupId = "3Ed23lNx"
				viewPath = "Page/Popups/warnings/warningsReminders/warningReminderBp"  # insert your view path here
				logger = system.util.getLogger("POPUP1")
				sessions = system.perspective.getSessionInfo(projectFilter="Production")
				
				#    now = system.date.now()
				path = ["[default]Popupfaults/Instrument air pressure/Instrument Air Pressure Low", "[default]Popupfaults/Instrument air pressure/air pressure value"]
				values = system.tag.readBlocking(path)
				value = values[0]
				actual = values[1]
				
				#    timeDiff = system.date.secondsBetween(value.timestamp, now)
				#	actual: %s, setpoint %s' %(actual.value, setpoint.value)
				description = 'Instrument air pressure low.<br> Air pressure is below 350 Kilopascals.'
				
				pageParams = {
				        'areaName':'',
				        'description':description,
				        'popupID': popupId,
				        'title': 'Instrument Air Pressure Low Warning',
				        'label1': '',
				        'label2': 'Pressure',
				        'units1':'',
				        'units2':'KPa',
				        'value1':'',
				        'value2':'%.2f'%(actual.value)
				        }
				
				for session in sessions:
				    for pageId in session.pageIds:
				        logger.info("{0} | {1}".format(session.id, pageId))
				        if session.userAgent != "<designer>":
				        	if value.value:
				        		system.perspective.openPopup(id=popupId, view=viewPath, params=pageParams, sessionId=session.id, pageId=pageId, title=title, showCloseIcon=False)
				        	else:
				        		# note no viewPath or param arg
								system.perspective.closePopup(id=popupId, sessionId=session.id, pageId=pageId)

The button on the popup has an action event script.

def runAction(self, event):
	# get the time right now
	now = system.date.now()
#	if self.view.params.popupID == "3Ed23lNx":
	timeack = getattr(self.session.custom, "instruAirPress.timeack")
	setattr(self.session.custom, "instruAirPress.timeack", now)

The issue with all of this is the setattr python method. In runtime (in the browser) it does not seem to set the timeack session custom property (except in preview mode in the designer). Hence, the countdown does not happen and popup does not appear again after 30s.

I have gotten this far, and I am stuck again, any ideas of what the problem could be?

Thank you.

Python's setattr cannot handle a chain of properties in the property name string.

Why do you need that if the property name string is a constant?

1 Like

I had initially tried setting the session custom parameter like so...

self.session.custom.instruAirPress.timeack = now

Again that method only worked from the designer but not during runtime, I had assumed that you had to use setattr() in order for the runtime to be able to write to the session parameter (as I saw this in another forum). In runtime I even attempted to read the timeack value by binding it a label component, and I can confirm that it is not being written to after i click the dismiss button on alarm popup.

That doesn't make any sense. How do you know it was that line that failed at runtime?