OPC Browse Tree

Hi everyone,

Besides dragging items from the OPC Browser into the Tag Browser and then using the Tag Browse Tree component is there any other way to give a client the ability to browse OPC tags? I’m thinking it’s possible with some logic, a bunch of system.opc.browse commands, and a tree view component but didn’t want to write all that if there was something obvious I missed.

Thanks,
Steve

I decided to write it and it wasn’t that difficult. In case it helps anyone in the future here’s the code. All you need is a window with a Tree View component. There’s an internalFrameActivated event on the window to populate the root with all your devices:

[code]# Get the current dataset from the tree view object
tvItems = system.gui.getParentWindow(event).getComponentForPath(‘Root Container.Tree View’).data

Convert it to a PyDataSet so we can iterate through it

pyDS = system.dataset.toPyDataSet(tvItems)

Delete the existing rows

for node in pyDS:
tvItems = system.dataset.deleteRow(tvItems, 0)

Get top-level opc tag data by device (the propertyChange event of the Tree View will have the logic needed to drill deeper)

opcServer = “Ignition OPC-UA Server”
nodeId = “Devices”
devices = system.opc.browseServer(opcServer, nodeId)

for device in devices:
device_name = device.getDisplayName()
device_type = device.getElementType()
device_nodeId = device.getServerNodeId()

device_items = system.opc.browseServer(opcServer, str(device_nodeId).replace('[' + opcServer + ']', ''))
	
for device_item in device_items:
	device_item_name = device_item.getDisplayName()
	device_item_type = device_item.getElementType()
	device_item_nodeId = device_item.getServerNodeId()
	
	if str(device_item_type).upper() == "OBJECT":
		# Create as folder with placeholder as item.  This allows tree to quickly be built but parent folder must be double-clicked for items to be populated
		icon = "Builtin/icons/16/about.png"		
		sep_char = system.gui.getParentWindow(event).getComponentForPath('Root Container.Tree View').separationCharacter
		tvItems = system.dataset.addRow(tvItems, [device_name + sep_char + device_item_name, "Placeholder", icon, "", "", "(double-click parent folder to refresh)", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])
	else:
		icon = "icon-tag.png"
		tvItems = system.dataset.addRow(tvItems, [device_name, device_item_name, icon, "", "", "", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])

#assign the new dataset to the component data property
system.gui.getParentWindow(event).getComponentForPath(‘Root Container.Tree View’).data = tvItems[/code]

Then there’s a propertyChange event on the Tree View:

[code]if event.propertyName == “selectedPath”:
selectedItem = event.newValue

# Get the current dataset from the tree view object
tvItems = event.source.parent.getComponent('Tree View').data

# Convert it to a PyDataSet so we can iterate through it
pyDS = system.dataset.toPyDataSet(tvItems)

i = 0		
# Find selected item in Tree View's dataset
for row in pyDS:
	# Get data for selected item 
	# - nodeId and elementType are custom columns added to the end of the Tree View's dataset																														
	path = row["path"] 
	text = row["text"]		
	nodeId = row["nodeID"]
	elementType = row["elementType"]
	if path == selectedItem and text == "Placeholder":			
		if elementType.upper() == "OBJECT":
			# It's a folder that was clicked on so drill into its items											
			opcServer = "Ignition OPC-UA Server"								
			device_items = system.opc.browseServer(opcServer, nodeId)
				
			# Following loop will add items to Tree View dataset and then delete placeholder record					
			for device_item in device_items:
				device_item_name = device_item.getDisplayName()
				device_item_type = device_item.getElementType()
				device_item_nodeId = device_item.getServerNodeId()
				
				if str(device_item_type) == "OBJECT":
					icon = "Builtin/icons/16/about.png"
					sep_char = system.gui.getParentWindow(event).getComponentForPath('Root Container.Tree View').separationCharacter
					tvItems = system.dataset.addRow(tvItems, [path + sep_char + device_item_name, "Placeholder", icon, "", "", "", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])
				else:
					icon = "icon-tag.png"
					tvItems = system.dataset.addRow(tvItems, [path, device_item_name, icon, "", "", "", "", "", icon, "", "", "", "", str(device_item_nodeId).replace('[' + opcServer + ']', ''), device_item_type])												
			
			tvItems = system.dataset.deleteRow(tvItems, i)	
						
			# Refresh Tree View with updated dataset
			event.source.parent.getComponent('Tree View').data = tvItems
			
			# For testing purposes, refresh table showing Tree View dataset
			table = event.source.parent.getComponent('Power Table')
			system.db.refresh(table, "data")				
		break
	else:
		i += 1[/code]

This only browses one level at a time so it doesn’t take long. The only thing I’m not pleased with is there’s no event on the Tree View I could find for expanding and collapsing nodes so you have to double-click to trigger the selectedPath propertyChange event to populate each level.

1 Like