How to preload nodes in the server OPC-UA address space?

Hello,

I am currently making a module (driver) in order to fetch data from an “exotic” PLC and expose them to another SCADA system by using the OPC-UA Server. The fetching part works fine but I have a problem accessing my data with the secondary SCADA.
This SCADA system can not suscribe to OPC Nodes if its translation calls fail. It simply stops its subscription process to a Node if it cannot be translated.

Like many drivers, mine does not load its nodes in the server address space unless it it necessary. It means that all translation calls will fail, as nodes are not loaded.

If I browse my driver’s nodes with the Designer OPC Browser or the Gateway Quick client, they seem to be loaded in the OPC-UA Server as the translation calls from the secondary SCADA work fine after that.

So I tried to implement a browsing function in my driver, but even if it seems to browse my nodes (so say my logs entries), it looks like it does not browse them the same way that the OPC Browser does as translation calls fail.

My question is : Is there a way to make an ‘browsing-like-an-OPC-Browser-that-would-browse-all-nodes’ function inside my driver ?

Thank you in advance for your answers.

Regards.

Rather than trying to simulate a browse coming from OPC it might be easier to just add all your nodes to the NodeManager when you start up. You must have or be able to obtain this information already, since you’re theoretically able to respond to a series of browse requests that would enumerate the nodes.

Thank you Kevin for your reply.

The nodes are currently already added to the NodeManager. I use something like that :

public static DataNode addNode(Map<String, BrowseNode> nodemap, FolderNode parent, String adress, String name, DataType type, List<DataNode> uaNodes, Variant val, NodeManager nodeManager) { DataNode node = new DataNode(adress, name); node.setType(type); node.setValue(val); parent.addChild((BrowseNode) node); nodemap.put(adress, (BrowseNode) node); uaNodes.add(node); return node; }

The thing is that even if they are in the NodeManager, they are not loaded in the adress space of the OPC-UA Server. It seems to be done once the nodes are browsed but not before. That is where lies my problem (or the secondary SCADA system’s one) : As they are not loaded, their paths cannot be translated to NodeId. The secondary SCADA simply stops its subscritpion process.

But, if the nodes have been browsed first, the traslation calls are successful and the secondary SCADA can manage to suscribe these nodes.

That is why I would like to be able to browse my nodes once they are all added to my NodeManager.

Regards.

Here is the subscription method of the secondary SCADA :

  • Session Create.
  • Node Paths to NodeID (using TranslateBrowsePathToNodeIds)
  • Subscription Create (using CreateSubscription)
  • Add Monitored Items

If the TranslateBrowsePathToNodeIds() call fails, the SCADA simply stops its subscription process for this node.

Hello,

I am still on this problem and cannot find any work around. Is it possible to simulate a browsing operation from the driver itself ?

Regards.

In your example your’e not actually adding anything to the NodeManager, just to the node map.

NodeManager#addNode() is how a Node is added to the address space; it’s the same thing drivers do in the buildNode() call. Pre-loading them instead of waiting for a browse to come in (which results in buildNode() being called) should allow them to be targeted by the TranslateBrowsePathsToNodeId call from the client.

Thank you Kevin for pointing that.

I added the addNode() method by taking inspiration from the driver code but, unfortunately, the nodes do not seem to be pre-loaded. And the TranslateBrowsePathToNodeId calls continue to fail.

Here is my new addNode method (not really clean yet but just for the tests):

	public static void addNode(Map<String, BrowseNode> nodemap, FolderNode parent, String adress, String name, DataType type, List<DataNode> uaNodes, List<Node> uaNodes_SBR, Variant val, NodeManager nodeManager, DriverContext driverContext)
	{	
	DataNode node = (DataNode)nodemap.get(adress);
        if ( node == null)
        {
            node = new DataNode(adress, name);
            node.setType(type);
            node.setValue(val);
            parent.addChild((BrowseNode) node);
            nodemap.put(adress, (BrowseNode) node);
        }
        NodeBuilderFactory builderFactory = driverContext.getNodeBuilderFactory();
        Node uaNode = builderFactory.newVariableNodeBuilder()
                                    .setNodeId(new NodeId(String.format("[%s]%s", new Object[]{name, adress}), 0))
                                    .setBrowseName(new QualifiedName(0, node.getDisplayName()))
                                    .setDisplayName(new LocalizedText(node.getDisplayName()))
                                    .setDataType(type.getNodeId())
                                    .setTypeDefinition(NodeIds.VariableNode_DataType.getNodeId())
                                    .setAccessLevel( EnumSet.of(AccessLevel.CurrentRead))
                                    .setUserAccessLevel(EnumSet.of(AccessLevel.CurrentRead))
                                    .buildAndAdd(nodeManager);

        nodeManager.addNode(uaNode);
        node.setUANode(uaNode);
        uaNodes.add(node);
        return;
}

Nodes are successfully created and the console reads no error at all. Still, the second SCADA calls to translate paths continue to fail.

I have tried many ways and i’m running out of ideas to make it work…
The most incomprehensible thing is that if I browse the node using the Quick client or the Designer OPC Browser make the translate calls successful.

I’m thinking of a dirty workaround that would periodically browse the nodes but I can not figure the methods called by the designer OPC Browser. Is there a way to get them ?

I can provide code, logs, screenshots, Teamviewer sessions to explain more accurately my problem but I really need to make it work.

Thank you in advance.

Regards.

I think what’s missing, assuming you’re calling addNode() for all your nodes on startup, is that when a browse happens references between nodes are also created, which is what gives these nodes in the address space any kind of structure.

After or during the addition of the nodes I think you need to also be calling NodeManager#addReference() to create whatever structure these are in.

				nodeManager.addReference(new Reference(
						nodeId,
						childNodeId,
						ReferenceType.HasComponent,
						ReferenceDirection.Forward));

Also - I think you should be using namespace index 1 for your NodeIds instead of 0.

Thank you for the reply.

I added the NodeMamager.addReference() in my addNode() method (which is actually called for all my nodes on startup).
It looks like :

        NodeId parentNodeId = new NodeId(parent.getBrowseName(),1);
        try {
        	nodeManager.addReference(new Reference(
        			parentNodeId,
    				uaNode.getNodeId(),
    				ReferenceType.HasComponent,
    				ReferenceDirection.Forward
    			));

That was added right after the “uaNodes.add(node);” in my previous code. Not really sure it is the correct syntax nor the expected arguments.

Anyway, it does not seem to preload much. I thought it came from my paths but even the nodes under the root nor the [Diagnostics] ones are preloaded…

I also changed the namespace to 1 instead of 0.

Hello,

I tried different ways to write the parent NodeId but none of them has been successful.

References seem to be correctly done (so says my logger) but the nodes aren’t pre-loaded.

I’m not sure what it is you’re doing differently here. The only thing that happens when you browse from the quick client is that the server creates the browse structure in its address space as you browse, which is basically what you’re mimicking by creating nodes, adding them to the node manager, then adding the references to the node manager. Nothing else happens during a browse of a device, and the translate browse paths logic doesn’t rely on anything other than nodes and references existing.

I don’t know how you’d trigger a browse from within a driver, but you could trigger one programmatically from scripting using the system.opc.browse or system.opc.browseServer

This function did not exist in 7.6.x. It explains why I could not find it.
Anyway, I migrated all my stuff (driver+Gateway) to 7.7 and that did the trick.

Thank you very very much. :thumb_left:

Best regards.