Jammed interface, unresponsive to component actions

Hello,

I am facing an issue with my interface when using .refreshBinding() on a table with approximately 100 rows. Here’s the situation:

I read the table data using system.tag.readBlocking() and process it to format the table, applying styles and descriptions. This part works quickly, taking about 1 second.

I added filtering options using several checkboxes to hide unnecessary rows. The filters are passed to the script, which processes the data and returns only the relevant rows. To apply the filters, I use a Refresh button with the function:
.refreshBinding("props.data").

The Problem:
After making several filter changes and clicking the Refresh button multiple times, the interface stops responding:

The Refresh button no longer triggers the update.
Table actions, such as cell clicks or value edits, also stop working.
I tried limiting the ability to trigger .refreshBinding() until the script finishes running, but this had no effect. The issue persists.

Questions:

What could cause the interface and table actions to freeze after multiple .refreshBinding() calls?
How can I properly diagnose what is blocking the interface or causing this behavior?
Are there any recommended debugging methods (e.g., using system logs, timers, or event diagnostics) for issues with .refreshBinding()?

Any advice or insights would be greatly appreciated!

Best Regards,
Michał

Show us the script.

Also, I'd pull the whole data into a custom property, then bind your table to this custom prop and apply the filters there. This way you won't need to use refreshBinding and you won't need to read the tags every time a filter is changed.

2 Likes


You should put the code here, not screen shots...
And also the code you call from your lib AJ3
image

You should btw turn the isActive to true before triggering the refresh binding, though i doubt this is the issue

1 Like
def process(filterOptions=None):
	
	COLOR_BACKGROUND= {
	    "No condition": "#FFFFFF00",
	    "Lack of communication" : "#FFFF00",
	    "Communication problem": "#AAAAAA",
	    "Grid problem": "#FF8A8A",
	    "Connector communication": "#D5D5D5",
	    "Status fault": "#FF0000",
	    "Status correct": "#00D900",
	    "Exclusion" : "#8A8AFF",
	    "Unknown" : "#000000"
	}
	
	COLOR_BORDER= {
	    "No condition": "#FFFFFF00",
	    "Communication problem": "#AAAAAA",
	    "Grid problem": "#FF8A8A",
	    "Status fault": "#FF0000",
	    "Status correct": "#00D900",
	    "Exclusion" : "#0000FF",
	    "Unknown" : "#000000"
	}
#	STATUS= {
#	    "No condition": 1,
#	    "Communication problem": 2,
#	    "Grid problem": 3,
#	    "Status fault": 4,
#	    "Status correct": 5,
#		"Exclusion" : 6,
#	    "Unknown" : 7
#	}

	filterToStatus = {
		"powerOffCritical": ["Status fault"],
		"noCommunication": ["Communication problem", "Lack of communication"],
		"powerOffConnector": ["Grid problem", "Connector communication"],
		"excludedByOperator": ["Exclusion"],
		"noCondition": ["No condition"]
	}

	styleInverter = {
		"backgroundColor": '#FFFFFF00',
	    "borderBottomLeftRadius": 10,
	    "borderBottomRightRadius": 10,
	    "borderTopRightRadius": 10,
	    "borderTopLeftRadius": 10,
	    "margin": 0,
	    "borderBottomStyle": "solid",
	    "borderLeftStyle": "hidden",
	    "borderRightStyle": "solid",
	    "borderTopStyle": "hidden",
	    "borderRightWidth": 4,
	    "borderRightColor": "#808080",
	    "borderBottomWidth": 4,
	    "borderBottomColor": "#808080"
	}
	
	styleEmpty = {
		"backgroundColor": "#FFFFFF00",
		"borderBottomLeftRadius": 0,
		"borderBottomRightRadius": 0,
		"borderTopRightRadius": 0,
		"borderTopLeftRadius": 0,
		"margin": 0
	}
	
	def irradianceAverage(data):
		
		countryIrradiance = {}
		
		for key, value in data.items():
			country = value["Information"]["Country"]
			irradiance = value["Condition"]["Pyranometer"]["Irradiance"]
			
			if not country:
				continue
			
			if isinstance(irradiance, (int, float, long)):
				if country not in countryIrradiance:
					countryIrradiance[country] = []
				
				countryIrradiance[country].append(irradiance)
				
		finalAverages = {}
			
		for country, irradianceList in countryIrradiance.items():
			
			filteredIrradiance = [value for value in irradianceList if value > 0]
			
			if filteredIrradiance:
				finalAverage = sum(filteredIrradiance) / len(filteredIrradiance)
				
			else:
				finalAverage = 0
				
			finalAverages[country] = finalAverage
			
		return finalAverages
		
	def informationItems(information):
		
	    informationItems = {}
	    
	    for key, value in information.items():
	        informationItems[key] = {"value": value}
	        
	    return informationItems
    
	def conditionItems(condition):
		
		conditionItems = {}
		connector = condition.get("Connector", {})
	    
		lackOfCommunication = condition.get("Alarm", {}).get("LackOfCommunication", {})
#		print lackOfCommunication
		LV = connector.get('LV', {})
		MV = connector.get('MV', {})
		
		if lackOfCommunication:
			print 'Brak komunikacji z farmą'
			conditionItems["LV"] = {"value": 3}
			conditionItems["MV"] = {"value": 3}
			
		else:
			if not LV.get('Communication', {}):
				if LV.get('State', {}) != 2:
					conditionItems["LV"] = {"value": LV.get('State', {})}
				else:
					conditionItems["LV"] = {"value": 3}
					
			elif LV.get('State', {}) is not None:
				conditionItems["LV"] = {"value": LV.get('State', {})}
				
			if not MV.get('Communication', {}):
				if MV.get('State', {}) != 2:
					conditionItems["MV"] = {"value": MV.get('State', {})}
				else:
					conditionItems["MV"] = {"value": 3}
			elif MV.get('State', {}) is not None:
				conditionItems["MV"] = {"value": MV.get('State', {})}
			
		
		return conditionItems

	def createValue(key, value):
		
		dictionaryID = {
				key: {
				"value": value
			}
		}
		
		return dictionaryID
		
	def inverterItems(siteParameters):
		
		inverterItems = {}
		
		
		def inverterStatus(inverter, condition, country):
			
			def irradianceProcessing(pyranometer, irradianceAverage):
				
				if pyranometer.get('Communication', {}):
					irradiance = pyranometer.get('Irradiance', {})
					
				else:
					irradiance = irradianceAverage
				
				return irradiance
				
			def connectorProcessing(connector):
				
				LV = connector.get('LV', {})
				MV = connector.get('MV', {})
				
				stateClose = True
				communication = True
				
				if MV.get('IsActive', {}) and MV.get('Communication', {}):
					if MV.get('State', {}) != 2:
						stateClose = False
					
				if LV.get('IsActive', {}) and LV.get('Communication', {}):
					if LV.get('State', {}) != 2:
						stateClose = False 
					
				if MV.get('IsActive', {}) and not MV.get('Communication', {}):
					communication = False
					
				if LV.get('IsActive', {}) and not LV.get('Communication', {}):
					communication = False
					
				return stateClose, communication
				
				
			#############################
			if False:
				communication = inverter.get('Communication')
				return "#00D900" if communication else "#AAAAAA"
			
			if True:
				
				pyranometer = condition.get('Pyranometer', {})
				country = information.get('Country', {})
				irradianceAverageValue = irradianceAverage.get(country)
				
				connector = condition.get('Connector', {})
				
				irradiance = irradianceProcessing(pyranometer, irradianceAverageValue)
				connector = connectorProcessing(connector)
				
				exclusion =  inverter.get('Exclusion', {}).get('Active', {})
				lackOfCommunication = condition.get('Alarm', {}).get('LackOfCommunication', {})
				
				Color = 'Unknown'
				
				if exclusion:
					Status =  'Exclusion'
					
				elif lackOfCommunication:
					Status = 'Lack of communication' 
				
				#grid
				elif not connector[0]:
					Status = 'Grid problem'
					
				elif not connector[1]:
					Status = 'Connector communication'
					
#					#No cummunication witch protection but inverter works good
#					if inverter.get('Communication', {}) and inverter.get('ActivePower', {}) > 0:
#						Status =  'Status correct'
#						
#					else:
#						Status = 'Connector communication'
				
				#condition to assessment
				elif irradiance < 2 or inverter.get('ActivePower', {}) == None:
					Status =  'No condition'
				
				#communication
				elif not inverter.get('Communication', {}):
					
					if not connector[0]:
						Status = 'Grid problem'
						
					Status =  'Communication problem'
					
				#grid + active power
				else:
					
					activePower = inverter.get('ActivePower', {})
					
					if activePower > ActivePowerTreshold:						
						Status =  'Status correct'
					
					else:
						
						if not connector[0]:
							Status = 'Grid problem'
						
						else:
							Status = 'Status fault'
					
			return Status
			
			
			
		def connectorSignalization(connector):
			
			LV = connector.get('LV', {})
			MV = connector.get('MV', {})
			
			if MV.get('IsActive', {}):# and MV.get('Communication', {}):
				if MV.get('State', {}) != 2:
					return 'MV'
				
			if LV.get('IsActive', {}): # and LV.get('Communication', {}):
				if LV.get('State', {}) != 2:
					return 'LV' 
				
			if MV.get('IsActive', {}) and not MV.get('Communication', {}):
				return 'BP'
				
			if LV.get('IsActive', {}) and not LV.get('Communication', {}):
				return 'BP'
				
			return False
			
		def dataFiltering(inverterStatusNameList):
			
			activeStatuses = set()
			
			if not any(filterOptions.values()):
				return True
			
			for key, active in filterOptions.items():
				
				if active and key in filterToStatus:
					activeStatuses.update(filterToStatus[key])
					
			if filterOptions.get("all", False):
				return True
			
			return any(status in activeStatuses for status in inverterStatusNameList)
			
		inverterStatusNameList = []
		
		for instance, inverter in siteParameters.get('Inverter', {}).items():
			
			condition = siteParameters.get('Condition', {})
			country = siteParameters.get('Information', {}).get('Country', {})
			
			inverterStatusName = inverterStatus(inverter, condition, country)
			inverterStatusNameList.append(inverterStatusName)
			
			styleInverterForInstance = styleInverter.copy()
			
			styleInverterForInstance["backgroundColor"] = COLOR_BACKGROUND[inverterStatusName]
			
			if inverterStatusName == 'Exclusion':
				styleInverterForInstance["borderRightColor"] = COLOR_BORDER[inverterStatusName]
				styleInverterForInstance["borderBottomColor"] = COLOR_BORDER[inverterStatusName]
				
			inverterInstance = "Inverter{}".format(instance)
			
			connector = condition.get('Connector', {})
			sign = connectorSignalization(connector)
			
			inverterItems[inverterInstance] = {
#				"active" : True,
				"value": int(instance) if not sign else sign,
				"style": styleInverterForInstance
			}
			
		if filterOptions and not dataFiltering(inverterStatusNameList):
			return None
			
		#complement to 40
		for instance in range(1, 41):
			
			inverterInstance = "Inverter{}".format(instance)
			
			if inverterInstance not in inverterItems:
				inverterItems[inverterInstance] = {
					"value": int(instance),
					"style": styleEmpty
				}
		
		
		return inverterItems
######################################################################################
	
	outputData = []
	ActivePowerTreshold = 0.1
	
	structurePath = '[.Frontend]Feature/InverterOverview.jsonValues'
	inputData = system.tag.readBlocking(structurePath)[0].value
	
	if len(dict(inputData)):
		irradianceAverage = irradianceAverage(inputData)
	
	for ID, siteParameters in inputData.items():
		
		output = {}
		
		output.update(createValue('ID', ID))
		output.update({"Empty": ""})
		
		if "Inverter" in siteParameters:
			
			inverter = siteParameters.get('Inverter',{})
			condition = siteParameters.get('Condition',{})
			information = siteParameters.get('Information',{})
			
			inverterItemsDict = inverterItems(siteParameters)
			
			if inverterItemsDict is not None:
				output.update(inverterItemsDict)
				output.update(createValue('NumberOfInverters', len(dict(inverter))))
			else:
				continue
			
		if "Information" in siteParameters:
			output.update(informationItems(siteParameters["Information"]))
			
		if "Condition" in siteParameters:
			output.update(conditionItems(siteParameters["Condition"]))
			
		outputData.append(output)
		
		
	return outputData


def transform(self, value, quality, timestamp):
	filterOptions = self.custom.filterOptions
	result = AJ3sR5.Feature.InverterOverview.Processing.process(filterOptions)
	self.custom.refreshIsActive = False
	return result
def runAction(self, event):
	if not self.parent.parent.parent.getChild("Table").custom.refreshIsActive:
		self.parent.parent.parent.getChild("Table").refreshBinding("props.data")
		self.parent.parent.parent.getChild("Table").props.selection.selectedColumn = None
		self.parent.parent.parent.getChild("Table").props.selection.selectedRow = None
		self.parent.parent.parent.getChild("Table").custom.refreshIsActive = True

uh thats is quite the lenghty script, with quite a fill loops.
Though you are only rly outputting a 100 lines so if the input tag doenst have that many more i dont rly see to much of a problem i guess...

Depending how often you need to refresh your data, you should probably do this to avoid having to run the long script as much as possible

1 Like

these if boolean seems weird, why the if xd did you forgot to add a variable here?

1 Like

This is a leftover. I was supposed to add something there, but I’m not using it for now. Basically, the script works perfectly fine, and its execution time is around 1–2 seconds. I don’t know why the refresh binding function causes my interface to freeze, even though I’ve ensured it can’t be called again before the script finishes generating the data.

After several refreshes, the interface stops responding to interactions, which is strange. I have to open a new tab and log in again...

If the interface is freezing, this indicates a problem in the frontend. Use the browser's built in diagnostics tools to figure out what's going on.