Property Change Null Pointer when Saving Designer

I’ve got a property change script that works well. For some reason I get a null pointer error in my script now. I’ve tried adding checks before it gets to the line that causes the issue but I’m still getting the error. Shouldn’t my if statements catch the null and just do nothing with it?

if event.propertyName == 'selectedTab':
	
	selectedTab = event.source.selectedTab
	
	if selectedTab and event.source.parent.getComponent('Tier2_'+selectedTab).tabData:
		event.source.parent.getComponent('Tier2').tabData = event.source.parent.getComponent('Tier2_'+selectedTab).tabData

This error never occurs during “normal runtime” but only when I save the designer - still somewhat annoying for the operators.

If I were to guess, the value for the Tier2 tabdata is going null when you save, and isn't refreshed in time for the event. The object itself is still there.

Maybe try something like:

if event.propertyName == 'selectedTab':
	selectedTab = event.source.selectedTab
	tabData = event.source.parent.getComponent('Tier2_'+selectedTab).tabData
	
	if selectedTab is not None and hasattr(tabData, '__len__'):
			event.source.parent.getComponent('Tier2').tabData = tabData

Personally, I've moved tab data to a document tag (or a folder of dataset tags before document tags were a thing).

So you’re saying some value within the tabData dataset is null and causing issue when it’s being accessed by vision? That’s word because this has been working for at least a year with no null pointer issue until last week.

But why is the issue with the script then and not just a temporary binding issue that resolves itself? The error popup specifically says the issue is with line 6. However, the error also says the issue is with the getValueAt function, which I am not explicitly calling.

Perhaps a try-catch would be the solution here. Try to assign it and do nothing when the error is caught. Although, we probably don’t want to catch every error and just throw it out.

In Vision, property assignments are synchronous. You are assigning a dataset to the Tier2 component's .tabData. Anything that component needs to do upon assignment (including retrieving content from the dataset) will happen within that line of your script. So the component's use of .getValueAt() can show up as if it is a script error.

You should inspect your dataset content.

I’ll check to see if I’ve got any null's in the dataset

I believe I found the issue. All of my tab strips are 10 tabs long. This one somehow got changed to 9. Adding in the 10th strip fixed it.

2 Likes

So, that was not my issue actually. I’ve recreated the issue in the designer now and I get a slightly different error.

java.lang.Exception: Error setting property to 'Dataset [13R ⅹ 24C]' for binding on
DockedNav.Root Container.Tier2_7.tabData
	at com.inductiveautomation.factorypmi.application.binding.AbstractPropertyAdapter.updateTarget(AbstractPropertyAdapter.java:333)
	at com.inductiveautomation.factorypmi.application.binding.CellUpdateAdapter.updateTarget(CellUpdateAdapter.java:214)
	at com.inductiveautomation.factorypmi.application.binding.AbstractPropertyAdapter.updateValue(AbstractPropertyAdapter.java:271)
	at com.inductiveautomation.factorypmi.application.binding.CellUpdateAdapter.eval(CellUpdateAdapter.java:185)
	at com.inductiveautomation.factorypmi.application.binding.CellUpdateAdapter.propertyChange(CellUpdateAdapter.java:200)
	at java.desktop/java.beans.PropertyChangeSupport.fire(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.awt.Component.firePropertyChange(Unknown Source)
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.localeChanged(PMITabStrip.java:210)
	at com.inductiveautomation.vision.api.client.components.model.AbstractVisionPanel.startupComponent(AbstractVisionPanel.java:222)
	at com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor$StartupVisitor.visit(ComponentVisitor.java:344)
	at com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor.walk(ComponentVisitor.java:95)
	at com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor.walk(ComponentVisitor.java:73)
	at com.inductiveautomation.factorypmi.application.FPMIWindow.startup(FPMIWindow.java:347)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace$DesigntimeWindowOpener.openWindow(WindowWorkspace.java:3880)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace.openWindow(WindowWorkspace.java:1528)
	at com.inductiveautomation.factorypmi.designer.model.navtree.WindowNode.open(WindowNode.java:260)
	at com.inductiveautomation.ignition.designer.navtree.model.AbstractResourceNavTreeNode.onDoubleClick(AbstractResourceNavTreeNode.java:428)
	at com.inductiveautomation.ignition.designer.navtree.NavTreePanel$MouseListener.lambda$mousePressed$1(NavTreePanel.java:722)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "com.inductiveautomation.ignition.common.Dataset.getValueAt(int, int)" is null
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.tabExistsAndIsEnabled(PMITabStrip.java:454)
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.setSelectedTab(PMITabStrip.java:480)
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.setTabData(PMITabStrip.java:544)
	at jdk.internal.reflect.GeneratedMethodAccessor88.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.factorypmi.application.binding.AbstractPropertyAdapter.updateTarget(AbstractPropertyAdapter.java:326)
	... 32 more

Ignition v8.1.35 (b2023120517)
Java: Azul Systems, Inc. 17.0.8

It seems that I may have another coalesce issue where a binding is being executed with a null property before the binding on that property can be executed.

**edit - That’s not it either. I have a cell update binding on the tabData dataset (for the Enabled column) that I was thinking was the issue, but I’ve coalesced the nulls to False and still get the error.

I now get one error for each of the bindings on this tab strip’s tabData .

java.lang.Exception: Error setting property to 'Dataset [13R ⅹ 24C]' for binding on
DockedNav.Root Container.Tier2_7.tabData
	at com.inductiveautomation.factorypmi.application.binding.AbstractPropertyAdapter.updateTarget(AbstractPropertyAdapter.java:333)
	at com.inductiveautomation.factorypmi.application.binding.CellUpdateAdapter.updateTarget(CellUpdateAdapter.java:214)
	at com.inductiveautomation.factorypmi.application.binding.AbstractPropertyAdapter.updateValue(AbstractPropertyAdapter.java:271)
	at com.inductiveautomation.factorypmi.application.binding.CellUpdateAdapter.eval(CellUpdateAdapter.java:185)
	at com.inductiveautomation.factorypmi.application.binding.CellUpdateAdapter.propertyChange(CellUpdateAdapter.java:200)
	at java.desktop/java.beans.PropertyChangeSupport.fire(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.awt.Component.firePropertyChange(Unknown Source)
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.localeChanged(PMITabStrip.java:210)
	at com.inductiveautomation.vision.api.client.components.model.AbstractVisionPanel.startupComponent(AbstractVisionPanel.java:222)
	at com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor$StartupVisitor.visit(ComponentVisitor.java:344)
	at com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor.walk(ComponentVisitor.java:95)
	at com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor.walk(ComponentVisitor.java:73)
	at com.inductiveautomation.factorypmi.application.FPMIWindow.startup(FPMIWindow.java:347)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace$DesigntimeWindowOpener.openWindow(WindowWorkspace.java:3880)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace.openWindow(WindowWorkspace.java:1528)
	at com.inductiveautomation.factorypmi.designer.workspace.WindowWorkspace$4.lambda$createPanels$2(WindowWorkspace.java:630)
	at com.inductiveautomation.ignition.designer.workspacewelcome.RecentlyModifiedTilePanel$Tile.lambda$new$1(RecentlyModifiedTilePanel.java:206)
	at com.inductiveautomation.ignition.client.util.gui.Listen$1.mousePressed(Listen.java:52)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(Unknown Source)
	at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
	at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.Component.processEvent(Unknown Source)
	at java.desktop/java.awt.Container.processEvent(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Unknown Source)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "com.inductiveautomation.ignition.common.Dataset.getValueAt(int, int)" is null
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.tabExistsAndIsEnabled(PMITabStrip.java:454)
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.setSelectedTab(PMITabStrip.java:480)
	at com.inductiveautomation.factorypmi.application.components.tabstrip.PMITabStrip.setTabData(PMITabStrip.java:544)
	at jdk.internal.reflect.GeneratedMethodAccessor88.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.factorypmi.application.binding.AbstractPropertyAdapter.updateTarget(AbstractPropertyAdapter.java:326)
	... 50 more

Ignition v8.1.35 (b2023120517)
Java: Azul Systems, Inc. 17.0.8

the dataset:

"#NAMES"
"NAME","DISPLAY_NAME","ENABLED","HIDDEN","HOVER_COLOR","SELECTED_IMAGE_PATH","SELECTED_IMAGE_HORIZONTAL_ALIGNMENT","SELECTED_IMAGE_VERTICAL_ALIGNMENT","SELECTED_FOREGROUND_COLOR","SELECTED_BACKGROUND_COLOR","SELECTED_FONT","SELECTED_GRADIENT_START_COLOR","SELECTED_GRADIENT_END_COLOR","UNSELECTED_IMAGE_PATH","UNSELECTED_IMAGE_HORIZONTAL_ALIGNMENT","UNSELECTED_IMAGE_VERTICAL_ALIGNMENT","UNSELECTED_FOREGROUND_COLOR","UNSELECTED_BACKGROUND_COLOR","UNSELECTED_FONT","UNSELECTED_GRADIENT_START_COLOR","UNSELECTED_GRADIENT_END_COLOR","USE_SELECTED_GRADIENT","USE_UNSELECTED_GRADIENT","MOUSEOVER_TEXT"
"#TYPES"
"str","str","B","B","clr","str","I","I","clr","clr","str","clr","clr","str","I","I","clr","clr","str","clr","clr","B","B","str"
"#ROWS","13"
"SLDG-10 SLUDGE OVERVIEW","<html><center>SLUDGE<br>TRANSFER","true","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"SLDG-20 PUMPDOWN MODE","<html><center>PUMP DOWN<br>MODES","true","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Phase_2B/10-RAS_WAS/RAS_WAS","<html><center>RAS/WAS","true","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Phase_2B/19-Digest/Overview","<html><center>PRIMARY<br>DIGESTERS","true","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Phase_2B/18-Dewatering/Dewatering","<html><center>DEWATERING","true","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Tab 6","<html><center>","false","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(0,0,0,0)","color(0,0,0,0)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Tab 7","<html><center>","false","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,0)","color(43,43,43,0)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Tab 8","<html><center>","false","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,0)","color(43,43,43,0)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Tab 9","<html><center>","false","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,0)","color(43,43,43,0)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Tab 10","<html><center>","false","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,0)","color(43,43,43,0)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"Tab 11","<html><center>SETTINGS","false","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"SLDG-91 TRENDS","<html><center>TRENDING","true","false","color(170,170,170,255)","","0","1","color(255,255,255,255)","color(61,61,61,255)","font(Dialog,bold,12)","color(218,221,224,255)","color(61,61,61,255)","","-1","-1","color(43,43,43,255)","color(218,221,224,255)","font(Dialog,PLAIN,12)","color(218,221,224,255)","color(192,197,202,255)","false","false",""
"LAST - Spacer - Do Not Delete","<html><font style=""font-size:0px"">long display name - tab size is deteremined by the maximum display name and the text padding. This display name is very long to ensure it is the longest in tab data, that way text padding between tab strips can be consistent.</font><html><html><html><html><html><html><html><html><html><html><html>","false","false","color(0,0,0,0)","","0","0","color(0,0,0,0)","color(0,0,0,0)","","color(0,0,0,255)","color(0,0,0,255)","","0","0","color(0,0,0,0)","color(0,0,0,0)","","color(0,0,0,255)","color(0,0,0,255)","false","false",""

Sorry, you are now on your own. I despise cell update bindings.

2 Likes

I thought you might say that. I will go another route and script the enabled column instead.

Do you know a simple way to set the entire column of values at once? There’s getColumnAsList but unfortunately there’s no setColumnFromList .

I ended up adding a custom dataset property to each tabStrip to hold the “active” tab information:

Every tabStrip has 11 tabs. Any of them can be used/hidden. So I’ve got an index for the “used” tabs and a list of conditions for that tab to actually be enabled.

property change script:

if event.propertyName == 'selectedTab':
	
	#gateway area
	gatewayArea = system.tag.readBlocking(['Areas/Station_Area'])[0].value
	
	#selected tab index
	selectedTab = event.source.selectedTab
	
	#get selected tabData and activeTabs
	selectedTabData = event.source.parent.getComponent('Tier2_'+selectedTab).tabData
	activeTabs = event.source.parent.getComponent('Tier2_'+selectedTab).activeTabs
	
	#validate datasets
	if selectedTab and selectedTabData and activeTabs:
		
		#assign active tabs
		event.source.parent.getComponent('Tier2').activeTabs = activeTabs
		#make list from tabIndexes column of dataset
		activeTabsList = activeTabs.getColumnAsList(activeTabs.getColumnIndex("tabIndex"))
		#make list from areas column of dataset. Requires conversion to a list of lists and conversion from string to int
		activeTabAreaList = [[int(area) for area in areas.split(',')] for areas in activeTabs.getColumnAsList(activeTabs.getColumnIndex("areas"))]
		
		#for each row in the selected dataset
		for row in range(selectedTabData.getRowCount()):
			#tab is active and the current area is in the list of areas for the tab
			if row in activeTabsList and gatewayArea in activeTabAreaList[activeTabsList.index(row)]:
				#enable the tab
				selectedTabData = system.dataset.setValue(selectedTabData,row,"ENABLED",True)
			else:
				#disable the tab
				selectedTabData = system.dataset.setValue(selectedTabData,row,"ENABLED",False)
		
		#assign the dataset to the tab strip
		event.source.parent.getComponent('Tier2').tabData = selectedTabData

Works great. No cell update bindings

1 Like