15:59, EEST
July 22, 2014
The application that I am creating uses BrowsePath references in order to identify OPC tags in the address space. In order to use most of the OPC functions in the Java SDK, I need to convert this to a NodeId. A previous post showed the following example:
String s = readInput(false);
final QualifiedName targetName = new QualifiedName(nodeId.getNamespaceIndex(), s);
BrowsePathTarget pathTarget = client.getAddressSpace().translateBrowsePathToNodeId(
nodeId,new RelativePathElement(Identifiers.HierarchicalReferences,false, true,targetName))[0];
println(“Target: ” + pathTarget.getTargetId());
println(“RemainingPathIndex: “+ pathTarget.getRemainingPathIndex());
Indicating that the above needed to be repeated for each “hop”. I use the above as a template to create the following code:
NodeId nodeId = Identifiers.RootFolder;
String pathString = “Objects.Server.CurrentTime”;
String[] parts = pathString.split(“\\.”);
for(String part : parts)
{
System.out.println(“Navigating “+part);
QualifiedName pathPart = new QualifiedName(nodeId.getNamespaceIndex(), part);
RelativePathElement relE = new RelativePathElement(Identifiers.HierarchicalReferences,false,true,pathPart);
BrowsePathTarget results[] = sampleClient.getAddressSpace().translateBrowsePathToNodeId(nodeId,relE);
nodeId = results[0].getTargetId().ID;
}
However, the above code does not work resulting in a “Bad_NoMatch (0x806F0000) “The requested operation has no match to return.”
I am guessing that I am not correctly assigning the NodeId of the “hop” back to my NodeId and thus it is unable to find the second part of the BrowsePath (i.e. “Server” in this case) in the address space.
Can someone please help me correct this code or provide me an example of how to correctly convert a complete BrowsePath (such as “Objects.Server.CurrentTime”) to the corresponding NodeId?
14:02, EEST
December 21, 2011
The error in your version is that you are using results[0].getTargetId().ID. You should use ‘client.getNamespaceTable().toNodeId(results[0].getTargetId())’ instead, to convert the received ExpandedNodeId to a NodeId. ID here is referring to a type identifier.
But, actually, you should use a single translateBrowsePathsToNodeIds call and specify all the “hops”, instead of just one. In SampleConsoleClient.browse() this is xeon as follows:
List<RelativePathElement> browsePath = new ArrayList<RelativePathElement>();
for (String s : browsePathString.split("/")) {
final QualifiedName targetName = QualifiedName
.parseQualifiedName(s);
browsePath.add(new RelativePathElement(
Identifiers.HierarchicalReferences, false, true,
targetName));
}
// The result may always contain several targets (if there are
// nodes with the same browseName), although normally only one
// is expected
BrowsePathTarget[] pathTargets;
try {
pathTargets = client
.getAddressSpace()
.translateBrowsePathToNodeId(
nodeId,
browsePath
.toArray(new RelativePathElement[0]));
…
12:26, EEST
July 22, 2014
Thank you for the code, it was very helpful. The example did not indicate what nodeId should be but from your explanation (and the documentation) this is the starting node. Since my browse paths (currently) start from the root node, I am using:
nodeId = Identifiers.RootFolder
Which seems to work. I then added the suggsted:
client.getNamespaceTable().toNodeId(results[0].getTargetId())
To resolve the results to a NodeId since the purpose of the code, in my case, was to obtain the corresponding nodeId.
Thanks.
18:46, EEST
July 22, 2014
What about the reverse? When a MonitoredDataItemListener generates a onDataChange(MonitoredDataItem sender, DataValue prevValue, DataValue value) call, how can I get the BrowsePath of the changed item?
The node is easy to get:
sender.getNodeId()
The BrowseName is also fairly easy to get:
client.getAddressSpace().getNode(sender.getNodeId()).getBrowseName().toString();
or
client.getAddressSpace().getNode(sender.getNodeId()).getBrowseName().getName();
However this only displays the last portion of the BrowsePath (for example CurrentTime from the BrowsePath Objects.Server.ServerStatus.CurrentTime).
The following code uses the BrowseUp function to trace the BrowsePath to the root. Is this the correct way to perform this conversion or is there a more efficient way (which does not require a BrowseUp function call for each hop)?
public String getBrowsePathFromNodeId(NodeId refNode)
{
try
{
String browsePath = super.getAddressSpace().getNode(nodeId).getBrowseName().getName();;
List results = null;
while(!refNode.equals(Identifiers.RootFolder))
{
results = client.getAddressSpace().browseUp(refNode);
refNode = client.getNamespaceTable().toNodeId(results.get(0).getNodeId());
if(!refNode.equals(Identifiers.RootFolder))
{
browsePath = results.get(0).getBrowseName().getName()+”.”+browsePath;
}
}
return browsePath;
}
catch(Exception e)
{
System.out.println(“Error: “+e.getMessage());
return “”;
}
}
10:25, EEST
December 21, 2011
There is no generic way to define the BrowsePath directly from the NodeId, as there is not necessarily a unique path even to the RootFolder. Every node may have several inverse references, in which case there may be several paths back to the root. browseUp() is probably the best for the job, but it is also a bit unreliable, since you don’t know which reference it will pick, if the node has several hierarchical inverse (i.e. upward) references.
The BrowsePath is primarily meant for finding the structural nodes of an object of a certain type, based on the structure of the type. Meaning that if you know that a DeviceType has a component called DeviceName, you can find the respective node from each instance of DeviceType using the TranslateBrowsePathsToNodeIds service. It is not defined so that each node would have a BrowsePath that could be used to identify it in the server.
In general, you should use the NodeIds to identify the nodes and not rely on the BrowseName or especially the BrowsePath too much – except for the structural example that I mentioned.
18:20, EEST
July 22, 2014
I understand that Nodes can have many paths back to root but I am still confused by the constant suggestion to use nodes as opposed to BrowsePaths. Especially if you are using a 3rd party Server where the NodeIds are set by the 3rd party server (i.e. you have no control over them).
I understand that “under the hood” OPC UA uses nodes but from a user point of view it is still, in many cases, more desirable to use BrowsePath because this is, typically, how data is entered into the OPC UA server. For example, if I use a Kepware OPC Server (which is a UA server) then I still add entries into the server using the familiar:
channel.device.signal
Where “channel” is typically the protocol, “device” is a specific instance of the protocol typically including a path (folders) structure and “signal” is the desired signal. As an example, one might configure:
Modbus.Station01.FareGate01.Opened
Modbus.Station01.FareGate02.Opened
Modbus.Station02.FareGate01.Opened
Modbus.Station02.FareGate02.Opened
Modbus.Station02.FareGate03.Opened
When an “Opened” signal changes, I want to be able to determine for which device the signal changed. Using BrowsePath this is easy to do. If, instead, I get a Node reference then I now would need some additional resources (memory map, lookup table) to relate the obscure NodeId to something useful to an operator.
Similarly, from the BrowsePath, I can very easily adjust alarm count settings. If I have a signal that just went into alarm state with a BrowsePath of:
Modbus.Station02.FareGate01.Opened
Then I can easily see that I need to increase the Alarm Count for Modbus, Station02 and Station02.FareGate01. If, instead, I got a NodeId then I would have to somehow determine what Alarm Counts that Node belongs to. As far as I can see, that means (at best) having the Alarm Count objects as children to the node and navigating to each of the node’s children to determine all the counts that need to be adjusted.
Now consider that one of the Fare Gates from Station 02 are moved to Station 01. With a BrowsePath solution, the signals are renamed and you are done because things like alarm counts are based on the BrowsePath (meaning they update automatically). Whereas with Nodes, I would need to re-link the Node’s children to ensure that the correct alarm counts are being updated which, in my opinion, is much more prone to error (if the NodeIds used by the server are more arbitrary IDs).
Just my 2 cents.
13:19, EEST
December 21, 2011
21:30, EET
November 3, 2016
unfortunately i’ve found the problem for some nodes
uaClient
.getAddressSpace()
.translateBrowsePathToNodeId(
Identifiers.RootFolder,
browsePathElements
.toArray(new RelativePathElement[0]));
fails to translate:
Which node do you wish to translate?
Use / to separate nodes in the browsePath, e.g. ‘Types/ObjectTypes/BaseObjectTyp
e/3:YourType’
where each element is a ‘parseable’ BrowseName, i.e. the namespaceIndex can be d
efined with a prefix, like ‘3:’
PLC42
com.prosysopc.ua.StatusException: Bad_NoMatch (0x806F0000) “The requested operat
ion has no match to return.” StatusCode=Bad_NoMatch (0x806F0000) “The requested
operation has no match to return.”
at com.prosysopc.ua.client.UaClient.checkOperationResult(Unknown Source)
at com.prosysopc.ua.client.AddressSpace.checkOperationResult(Unknown Sou
rce)
at com.prosysopc.ua.client.AddressSpace.translateBrowsePathToNodeId(Unkn
own Source)
at com.prosysopc.ua.samples.client.SampleConsoleClient.browse(SampleCons
oleClient.java:516)
at com.prosysopc.ua.samples.client.SampleConsoleClient.browse(SampleCons
oleClient.java:539)
at com.prosysopc.ua.samples.client.SampleConsoleClient.browse(SampleCons
oleClient.java:494)
at com.prosysopc.ua.samples.client.SampleConsoleClient.mainMenu(SampleCo
nsoleClient.java:1385)
at com.prosysopc.ua.samples.client.SampleConsoleClient.main(SampleConsol
eClient.java:154)
10:36, EET
April 17, 2013
Hello Rafal,
If you input “PLC42”, the default behaviour is that the namespaceindex is assumed to be 0. This in turn means the standard namespace where your own custom nodes do not reside.
So, you need to specify the NamespaceIndex in the call.
As an example, you can try resolving the node id of MyObjectsFolder in the SampleConsoleServer. From root node, trying to resolve browse path Objects/MyObjects ends up in Bad_NoMatch exception. However, if you try Objects/2:MyObjects you will see “Target: ns=2;s=MyObjectsFolder” as the result.
As Jouni mentioned before, the translateBrowsePathToNodeId service is primarily meant for finding the structural nodes of an object of a certain type, based on the structure of the type.
Most Users Ever Online: 1919
Currently Online:
66 Guest(s)
Currently Browsing this Page:
1 Guest(s)
Top Posters:
Heikki Tahvanainen: 402
hbrackel: 144
rocket science: 88
pramanj: 86
Francesco Zambon: 83
Ibrahim: 78
Sabari: 62
kapsl: 57
gjevremovic: 49
Xavier: 43
Member Stats:
Guest Posters: 0
Members: 737
Moderators: 7
Admins: 1
Forum Stats:
Groups: 3
Forums: 15
Topics: 1524
Posts: 6450
Newest Members:
fannielima, kristiewinkle8, rust, christamcdowall, redaahern07571, nigelbdhmp, travistimmons, AnnelCib, dalenegettinger, howardkennerleyModerators: Jouni Aro: 1026, Pyry: 1, Petri: 0, Bjarne Boström: 1026, Jimmy Ni: 26, Matti Siponen: 346, Lusetti: 0
Administrators: admin: 1