Caching of browse nodes in OPC-UA server

Hi again,

i’m still working on my OPC-UA driver. It’s basically a tcp driver with a customizable binary message format. Now, for convenience i use a describing browse name in addition to the opc-ua address. When i change this name in the config ui and do not touch the address, the browse name in clients is not updated before i restart the opc-ua module.
In addition, if i change the address and the name, the browse is refreshed as expected. But when i change the address back to the old value, the name is set to the value that was assigned to this address at the last server restart.
It seems that there is a cache in the opc-ua module, that is not cleared when a driver module restarts. Would it be a big problem to change this behaviour?

Nodes that a driver adds to the address space (using NodeManager and NodeBuilderFactory that you get during initialization) are only removed if you do so explicitly.

As a best practice, your driver should remove all nodes it adds during its shutdown() call. Not doing so means you’ll leak Nodes into the address space.

If you’re changing things dynamically, without requiring your driver to be restarted, then do a remove/add.

Ah, thank you. I missed that one in the modbus example.

Just to make sure:

i extended from NodeMapDriver and did not touch buildNode by now. If i got it right, i have to override buildNode and keep a reference to all nodes added to the nodeManager?
In this case it might be more clear to make buildNode abstract in the NodeMapDriver.

Another question: What is the function of the NodeManager (It is not in the javadocs)? From the modbus example i see that it only knows the browse name and the display name, but not the address. Does this mean browse name or display name has to be unique?

Thank`s for your help.

I had assumed you were extending AbstractDriver directly…

Ooph, I wish NodeMapDriver didn’t exist anymore. It’s old and a little poorly done… but that’s OK. It seems that NodeMapDriver is not being a good citizen and removing Nodes on shutdown, I’ll have to fix that.

So earlier when I said you need to remove Nodes that get added on shutdown()… that is the case when extending AbstractDriver directly because when doing that you’re also the one who adds them. You have to do it now as well, since NodeMapDriver is essentially broken in that manner, but yeah.

Right now on shutdown() you’ll have to iterate through all the BrowseNode values in the the Map returned by getNodeMap() and use the Node you get from getUANode() to call NodeManager#removeNode().

As for the purpose of NodeManager, it’s basically your interface to the UA address space as a Driver. Unfortunately, it’s got a number of extra calls on it right now that you won’t ever need as a Driver, since it’s used in the server as well. You should really only ever need addNode(), removeNode(), and maybe findeNode().

I’m not sure what you’re talking about when you refer to browse name and display name in the context of a NodeManager. Are you talking about the VariableNodeBuilder you get from NodeBuilderFactory when constructing and add the UA Node objects in the Modbus buildNode() call?

Errr, while fixing this I realized that BrowseNode#setUANode() is never being called in NodeMapDriver either, so you won’t be able to use getUANode() to remove it like I explained above.

Should be fixed in next 7.4 release.

I think what i’m missing is the link between the uaNode and the internal browse node. I have seen the uaNode field in BrowseNode, but it is always null. In the modbus example there is a separate List uaNodes keeping track of all added nodes. Should i assign the uaNode field in the BrowseNode instead?

When the uaNode is added to the NodeManager via buildAndAdd (i have copied this from the modbus example now), i set browseName and displayName before, but no address, so what i don’t understand is how the uaNode is connected to the browseNode. Or is there a map connecting the address and nodeId passed to buildNode?

Node uaNode = builderFactory
			.newVariableNodeBuilder()
			.setNodeId(nodeId)
			.setBrowseName(new QualifiedName(1, node.getBrowseName()))
			.setDisplayName(new LocalizedText(node.getDisplayName()))
			.setValueRank(
				vNode.getArrayLength() > 0 ? ValueRank.OneDimension : ValueRank.Scalar)
			.setAccessLevel(EnumSet.of(AccessLevel.CurrentRead))
			.setTypeDefinition(NodeIds.VariableNode_DataType.getNodeId())
			.buildAndAdd(nodeManager);
uaNodes.add(uaNode);

First i planned extending AbstractTagDriver, but the NodeMapDriver already handles the problems with asynchron operations, though i’m not sure about the overhead caused by the continously created requests. With AbstractTagDriver, i can not get a ExecutionManager, because DriverContext expects an AbstractDriver in getPrivateExecutionManager().
I think it is not a good idea to block readItems() while an incoming message is evaluated or transmit outgoing messages directly in writeItems()?

There isn’t a direct link between the BrowseNode class in the API and the UA Node class. BrowseNode is basically a legacy holdover that was used in some driver implementations to keep track of its tags/nodes, but has no relationship to UA Node other than having similar fields.

During buildNode(), the address has essentially been set for you as a part of the Node’s NodeId. The server uses a special format for NodeId’s to indicate that a NodeId belongs to a device: This is the “[DeviceName]AddressGoesHere” format you see in the item path when creating/viewing a SQLTag. This is why buildNode() provides a NodeId to use when building the Node; it’s already been formatted with the device name and address for you.

And you can get an ExecutionManager from the GatewayContext: driverContext.getGatewayContext().createExecutionManager(). The call to create one on DriverContext has some legacy surrounding it as well and doesn’t have to be used.

Thank you again, especially for the impressive response time.