10:38, EEST
January 31, 2021
Hello,
I’m presently utilizing the Prosys OPC UA SDK as part of my thesis project at the Aalto University of Technology. As part of my thesis I am implementing a simple client program which can invoke methods on a device server. I have recently however run into issues with the UAClient call method.
Presently my implementation traverses the server address space by first retrieving the address space with the UAClient.getAddressSpace() method. This is then traversed by using the browse() method and iterating through the list of references. The nodeID of the wanted object form this is then saved to a temporary variable and used to travel deeper into the address space hierarchy with the browse method.
This is done until the wanted object and method are reached. The NodeIds for both are saved however when attempting to invoke a method on the server with the client call method the method instead throws a MethodCallStatusException with the following information: “Bad_MethodInvalid (0x80750000) “The method id does not refer to a method for the specified object.” StatusCode=Bad_MethodInvalid (0x80750000) “The method id does not refer to a method for the specified object.””
I have checked that both the node id and method id that are given as parameters for the call method are correct in addition to checking that the hierarchy is as intended by using the OPC UA Browser. Any ideas for troubleshooting would be greatly appreciated as I’ve run out of ideas.
Thank you for any assistance you may be able to provide.
11:20, EEST
April 3, 2012
Hi,
Is the Method (methodId) directly below the Object (objectId) or “more deep”? Rest of the answer assumes “more deep”.
You can only call a Method of an Object it is, i.e. the Method must be directly below the Object. Techinically due to erratas and spec updates in some cases it could also be just defined in the type, but not all servers support this (anyway, this is the relevant spec part: https://reference.opcfoundation.org/Core/docs/Part4/5.11.2/).
So in general you can think that the objectId must be the source node of the HasComponent for which the Method node is the target node (i.e. is the methodId).
Since you mention “a device server”, I could interpret that to mean the server uses the DI companion specification information model. That is a bit weird model that has the “MethodSet” (https://reference.opcfoundation.org/DI/docs/4.3/) folder below the top-lvl instance node that had the methods. In that case the “MethodSet” folder’s NodeId must be used as the objectId (and not the top-lvl one).
16:41, EEST
January 31, 2021
The method being called is a direct child object yes. In practice I’m saving the node id of the object into a temporary variable and then retrieving the methods with the GetMethods() method. While I am using the DI-companion specification I’ve opted against a MethodSet for these specific objects. What makes things more confusing is that the method can be invoked just fine with the Browser which narrows the issue down to my implementation of the client.
I don’t know if this will help but here is the relevant section for browsing the address space (barring sections commented out during testing and variable declarations):
//Connect to the chosen server
connect(tmpClient);
tmpClient.getAddressSpace().setMaxReferencesPerNode(1000);
tmpClient.getAddressSpace().setReferenceTypeId(Identifiers.HierarchicalReferences);
//Browse the address space to get the namespace table and device skills
tmpSpace = tmpClient.getAddressSpace();
refDescs = tmpSpace.browse(Identifiers.ObjectsFolder);
tmpTable = tmpSpace.getNamespaceTable();
//Iterate to find the device Set
for(ReferenceDescription tempRef : refDescs) {
if(tempRef.getDisplayName().getText().equals(“DeviceSet”)) {
tmpId = tmpTable.toNodeId(tempRef.getNodeId());
}
}
//Iterate again to find Device Skills
refDescs = tmpSpace.browse(tmpId);
for(ReferenceDescription tempRef : refDescs) {
if(tempRef.getDisplayName().getText().contains(“DeviceSkills”)) {
tmpId = tmpTable.toNodeId(tempRef.getNodeId());
}
}
//Get refs from DeviceSkills to build list of skills
skillRefs = new ArrayList();
refDescs = tmpSpace.browse(tmpId);
for(ReferenceDescription tempRef : refDescs) {
if(tempRef.getNodeClass().toString().equals(“Object”)) {
skillRefs.add(tempRef);
}
}
//Browse the methods
for (ReferenceDescription tempRef : skillRefs) {
if (tempRef.getDisplayName().getText().contains(skillName)) {
skillId = tmpTable.toNodeId(tempRef.getNodeId());
skillMethods = tmpSpace.getMethods(skillId);
}
}
//Get method id
for(UaMethod tempSkill : skillMethods) {
if (tempSkill.getDisplayName().getText().contains(action)) {
testId = tempSkill.getNodeId();
}
}
//Call selected method
Variant[] outputs = tmpClient.call(skillId,testId);
System.out.println(“Method output: ” + outputs);
I must apologize for the long-windedness however at the time I could not think of a more elegant method of retrieving the selected object and method except for browsing the address space a layer at a time. SkillName and action are strings which are previously selected names of objects in the hierarchy. I would also like to apologize for the formatting as the forums are eating my indentation for some unknown reason.
17:14, EEST
April 3, 2012
Unless performance is really critical you are most likely easier by using the “UaNode”-based API of the SDK (you sort of are using it with the getMethods of AddressSpace as UaMethod is also an UaNode).
e.g. something like:
// This is the "well known" NodeId for the DeviceSet object final UaNodeId deviceSetId = UaNodeId.from("http://opcfoundation.org/UA/DI/", UnsignedInteger.valueOf(5001)); final UaNode deviceSet = client.getAddressSpace().getNode(deviceSetId.asExpandedNodeId()); for (UaReference ref : deviceSet.getReferences(Identifiers.HierarchicalReferences, false)) { UaNode device = ref.getOppositeNode(deviceSet); for (UaMethod method : client.getAddressSpace().getMethods(device.getNodeId())) { // something here } }
(Note that I didn’t run this code, but it is as an example)
Also, in general it is not a good idea to make conditional logic using the DisplayName Attribute, as it (at least in theory) could be in some other locale. BrowseName Attribute should be used instead.
Also, the formatting of code might work better with ‘pre’ html tags, though yes it is not that great either. We should get a better forum implementation…
11:31, EEST
January 31, 2021
It would appear that a part of my initial diagnosis of the issue was wrong. I made minor modifications so that instead of calling the method with the saved NodeIds as input paramters I instead re-retrieved the nodes from the address space before getting their NodeId:s. I am still getting the same Bad_MethodInvalid call status exceptions however quite confusingly the server method handler is still executing the method that was called.
I will continue tweaking the code to see if I can find a solution. Thank you for taking the time to help with troubleshooting.
EDIT: I have found the cause. It was actually nothing to do with the method call itself. The device server method handler was set to return a failed result in case it was unable to contact the PLC corresponding to the server. For some reason the client interpreted this instead as a MethodInvalid error. This issue is resolved and was entirely due to a mistake (maybe?) I made in an entirely different part of the code.
Most Users Ever Online: 1919
Currently Online:
26 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: 735
Moderators: 7
Admins: 1
Forum Stats:
Groups: 3
Forums: 15
Topics: 1523
Posts: 6449
Newest Members:
rust, christamcdowall, redaahern07571, nigelbdhmp, travistimmons, AnnelCib, dalenegettinger, howardkennerley, Thomassnism, biancacraft16Moderators: Jouni Aro: 1026, Pyry: 1, Petri: 0, Bjarne Boström: 1026, Jimmy Ni: 26, Matti Siponen: 346, Lusetti: 0
Administrators: admin: 1