Avatar

Please consider registering
guest

sp_LogInOut Log In sp_Registration Register

Register | Lost password?
Advanced Search

— Forum Scope —




— Match —





— Forum Options —





Minimum search word length is 3 characters - maximum search word length is 84 characters

sp_Feed Topic RSS sp_TopicIcon
Converting BrowsePath To NodeIds
August 20, 2014
15:59, EEST
Avatar
OndrejBlazek
Member
Members
Forum Posts: 19
Member Since:
July 22, 2014
sp_UserOfflineSmall Offline

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?

August 25, 2014
14:02, EEST
Avatar
Jouni Aro
Moderator
Moderators
Forum Posts: 1010
Member Since:
December 21, 2011
sp_UserOfflineSmall Offline

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:

String browsePathString = readInput(false);

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]));

August 26, 2014
12:26, EEST
Avatar
OndrejBlazek
Member
Members
Forum Posts: 19
Member Since:
July 22, 2014
sp_UserOfflineSmall Offline

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.

August 26, 2014
18:46, EEST
Avatar
OndrejBlazek
Member
Members
Forum Posts: 19
Member Since:
July 22, 2014
sp_UserOfflineSmall Offline

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 “”;
}
}

August 27, 2014
10:25, EEST
Avatar
Jouni Aro
Moderator
Moderators
Forum Posts: 1010
Member Since:
December 21, 2011
sp_UserOfflineSmall Offline

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.

September 24, 2014
18:20, EEST
Avatar
OndrejBlazek
Member
Members
Forum Posts: 19
Member Since:
July 22, 2014
sp_UserOfflineSmall Offline

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.

September 25, 2014
13:19, EEST
Avatar
Jouni Aro
Moderator
Moderators
Forum Posts: 1010
Member Since:
December 21, 2011
sp_UserOfflineSmall Offline

Yes, I understand and I also agree. Unfortunately, though, there is no generic way to offer the BrowsePath for use. Unless you manage to create a suitable BrowsePath/NodeId mapping in your client application.

November 3, 2016
21:30, EET
Avatar
rafal.zurawski
New Member
Members
Forum Posts: 1
Member Since:
November 3, 2016
sp_UserOfflineSmall Offline

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)

November 4, 2016
10:36, EET
Avatar
Heikki Tahvanainen
Moderator
Members

Moderators
Forum Posts: 402
Member Since:
April 17, 2013
sp_UserOfflineSmall Offline

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.

Forum Timezone: Europe/Helsinki

Most Users Ever Online: 518

Currently Online:
12 Guest(s)

Currently Browsing this Page:
1 Guest(s)

Top Posters:

hbrackel: 135

pramanj: 86

Francesco Zambon: 81

rocket science: 77

Ibrahim: 76

Sabari: 62

kapsl: 57

gjevremovic: 49

Xavier: 43

TimK: 41

Member Stats:

Guest Posters: 0

Members: 682

Moderators: 16

Admins: 1

Forum Stats:

Groups: 3

Forums: 15

Topics: 1467

Posts: 6261

Newest Members:

digitechroshni, LouieWreve, Kickbiche, karrimacvitie5, graciela2073, sagarchau, elviralangwell4, Donnavek, Eddiefauth, DonaldPooma

Moderators: Jouni Aro: 1010, Otso Palonen: 32, Tuomas Hiltunen: 5, Pyry: 1, Petri: 0, Bjarne Boström: 983, Heikki Tahvanainen: 402, Jukka Asikainen: 1, moldzh08: 0, Jimmy Ni: 26, Teppo Uimonen: 21, Markus Johansson: 42, Niklas Nurminen: 0, Matti Siponen: 321, Lusetti: 0, Ari-Pekka Soikkeli: 5

Administrators: admin: 1